From 8e379a23e047f35c1ac1d177744b0a6c731f335a Mon Sep 17 00:00:00 2001 From: mikedeboer Date: Thu, 8 Dec 2011 16:39:02 +0100 Subject: [PATCH 01/69] refactored the splitview plugin and enabled it as default cloud9 plugin to load --- client/ext/splitview/grids.js | 350 ++++++++++++++++ client/ext/splitview/splits.js | 322 +++++++++++++++ client/ext/splitview/splitview.js | 635 +++++------------------------- server/cloud9/ide.js | 3 +- 4 files changed, 772 insertions(+), 538 deletions(-) create mode 100644 client/ext/splitview/grids.js create mode 100644 client/ext/splitview/splits.js diff --git a/client/ext/splitview/grids.js b/client/ext/splitview/grids.js new file mode 100644 index 00000000000..e988dc2a4a6 --- /dev/null +++ b/client/ext/splitview/grids.js @@ -0,0 +1,350 @@ +/** + * Show a split view; two editors next to each other in one tab + * + * @copyright 2010, Ajax.org B.V. + * @author Mike de Boer + * @license GPLv3 + */ + +define(function(require, exports, module) { + +var GridLayouts = { + "3cols": { + "ascii": + "+--+--+--+\n" + + "| | | |\n" + + "+--+--+--+", + // hbox + // --> splitter + // --> splitter + "struct": { + "hbox": [ + "splitter", + "splitter" + ] + }, + "insertPoints": [ + "hbox/splitter[1]", + "hbox/splitter[2]", + "hbox" + ] + }, + "3rows": { + "ascii": + "+-----+\n" + + "| |\n" + + "+-----+\n" + + "| |\n" + + "+-----+\n" + + "| |\n" + + "+-----+", + // vbox + // --> splitter + // --> splitter + "struct": { + "vbox": [ + "splitter", + "splitter" + ] + }, + "insertPoints": [ + "vbox/splitter[1]", + "vbox/splitter[2]", + "vbox" + ] + }, + "2cols2rows": { + "ascii": + "+--+--+\n" + + "| | |\n" + + "| +--+\n" + + "| | |\n" + + "+--+--+", + // --> hbox + // --> splitter + // --> vbox + // --> splitter + "struct": { + "hbox": { + "splitter": 1, + "vbox": { + "splitter": 1 + } + } + }, + "insertPoints": [ + "hbox/splitter", + "hbox/vbox/splitter", + "hbox/vbox" + ] + }, + "col2rowscol": { + "ascii": + "+--+--+\n" + + "| | |\n" + + "+--+ |\n" + + "| | |\n" + + "+--+--+", + // --> hbox + // --> vbox + // --> splitter + // --> splitter + "struct": { + "hbox": { + "vbox": { + "splitter": 1 + }, + "splitter": 1 + } + }, + "insertPoints": [ + "hbox/vbox/splitter", + "hbox/vbox", + "hbox" + ] + }, + "row2colsrow": { + "ascii": + "+--+--+\n" + + "| | |\n" + + "+--+--+\n" + + "| |\n" + + "+-----+", + // vbox + // --> hbox + // --> splitter + // --> splitter + "struct": { + "vbox": { + "hbox": { + "splitter": 1 + }, + "splitter": 1 + } + }, + "insertPoints": [ + "vbox/hbox/splitter", + "vbox/hbox", + "vbox" + ] + }, + "2rows2cols": { + "ascii": + "+-----+\n" + + "| |\n" + + "+--+--+\n" + + "| | |\n" + + "+--+--+", + // vbox + // --> splitter + // --> hbox + // --> splitter + "struct": { + "vbox": { + "splitter": 1, + "hbox": { + "splitter": 1 + } + } + }, + "insertPoints": [ + "vbox/splitter", + "vbox/hbox/splitter", + "vbox/hbox" + ] + } +}; + +var GridNames = Object.keys(GridLayouts); + +var defaultGrid = exports.DEFAULT_GRID = "3cols"; + +/** + * Create the available grids + */ +exports.init = function(gridLayout) { + gridLayout = gridLayout || defaultGrid; + console.log("init called", gridLayout); + createGridNodes(gridLayout); + return gridLayout; +}; + +exports.get = function(name) { + return GridLayouts[name]; +}; + +exports.update = function(gridLayout, split) { + var grid = GridLayouts[gridLayout]; + + console.log("update: ", split.pages[0].$pHtmlNode, grid.node, grid.node.parentNode, grid.node.$pHtmlNode); + grid.node.show(); + // attach the grid layout to the first page of the splitview... + var page = split.pages[0]; + var amlPage = page.fake ? page.relPage : page; + amlPage.appendChild(grid.node); + + console.log(split.editors.map(function(editor){return editor.$ext;}), grid.insertPoints); + var i = 0; + var l = split.editors.length; + for (; i < l; ++i) + insertEditorAt(grid.node, split.editors[i], [].concat(grid.insertPoints[i])); + + // hide splitters that we don't need to see anymore + if (!grid.splitters) + grid.splitters = grid.node.selectNodes("splitter"); + for (i = grid.splitters.length - 1; i >= l - 1; --i) + grid.splitters[i].hide(); +}; + +exports.show = function(gridLayout) { + GridNames.forEach(function(name) { + var grid = GridLayouts[name]; + if (!grid.node) + return; + + if (name == gridLayout) { + grid.node.show(); + } + else { + grid.node.hide(); + if (grid.node.parentNode != apf.document.body) + apf.document.body.appendChild(grid.node); + } + }); +}; + +exports.hide = function(gridLayout) { + gridLayout = gridLayout || defaultGrid; + var grid = GridNames[gridLayout]; + if (!grid || !grid.node) + return; + + grid.node.hide(); + apf.document.body.appendChild(grid.node); +}; + +function createNodes(struct, splitters, parent) { + // if struct.node already exists, we already passed this function... + if (struct.node) + return; + + parent = parent || apf.document.body; + (apf.isArray(struct) ? struct : Object.keys(struct)).forEach(function(nodeName) { + var options = {}; + if ("vbox|hbox".indexOf(nodeName) > -1) { + if (parent === apf.document.body) { + options.visible = false; + options.anchors = "0 0 0 0"; + } + else { + options.flex = 1; + options.padding = "0"; + } + } + else if (nodeName == "splitter") { + options.visible = false; + options.skin = "darksplitter"; + } + + var node = parent.appendChild(new apf[nodeName](options)); + if (nodeName == "splitter"){console.log("GOT SPLITTER"); + splitters.push(node);} + // if we just appended the main node to the document, set it as the grid's + // main node + if (parent === apf.document.body) + struct.node = node; + // recurse down the structure's tree + if (struct[nodeName] && struct[nodeName] !== 1) + createNodes(struct[nodeName], splitters, node); + }); +} + +function createGridNodes(name) { + name = name || defaultGrid; + var blueprint = GridLayouts[name]; + if (!blueprint) + throw new Error("Grid layout with name '" + name + "' not found!"); + + // preparse paths out of the insertPoints... + blueprint.insertPoints = blueprint.insertPoints.map(function(insertPoint) { + if (typeof insertPoint == "string") + return insertPoint.splitSafe("\\/", null, true); + return insertPoint; + }); + //console.log("blueprint.insertPoints: ", blueprint.insertPoints); + + var splitters = []; + createNodes(blueprint.struct, splitters); + if (!blueprint.node) + blueprint.node = blueprint.struct.node; + //@todo fix the splitter enumeration + //if (!blueprint.splitters) + // blueprint.splitters = splitters; +} + +function insertEditorAt(parent, editor, insertPoint) { + //console.log("insertEditorAt",parent, editor, insertPoint); + var inserted = false; + //var count = 0; + while (!inserted) { + //console.log("round", ++count, "tags:",parent.tagName, insertPoint[0]); + if (parent.tagName.indexOf(insertPoint.shift()) == -1) { + console.log(parent.tagName, insertPoint); + throw new Error("No valid insertion point found for editor"); + } + + // do we need to keep searching down the path? + if (insertPoint.length > 1) { + for (var i = 0, l = parent.childNodes.length; i < l; ++i) { + if (parent.childNodes[i].tagName.indexOf(insertPoint[0]) == -1) + continue; + console.log("setting parent to...",parent.childNodes[i]); + parent = parent.childNodes[i]; + break; + } + } + // path found, try to insert it! + else { + if (!insertPoint.length) { + parent.appendChild(editor); + inserted = true; + } + // finding a splitter means 'insert before it!' + else if (insertPoint[0].indexOf("splitter") > -1) { + var m = insertPoint[0].match(/\[(\d)\]/); + var splitterIdx = m && m[1] ? parseInt(m[1], 10) - 1 : 0; + var idx = -1; + //console.log("trying to find splitter at index", splitterIdx, m, parent.childNodes.map(function(node) {return node.tagName;})); + for (var i = 0, l = parent.childNodes.length; i < l && !inserted; ++i) { + if (parent.childNodes[i].tagName.indexOf("splitter") == -1 + || splitterIdx !== ++idx) { + continue; + } + parent.insertBefore(editor, parent.childNodes[i]); + inserted = true; + } + if (!inserted) + throw new Error("Could not find the splitter to insert before!"); + } + // otherwise it needs to be appendChild'ed to a vbox or hbox + else { + for (var i = 0, l = parent.childNodes.length; i < l && !inserted; ++i) { + if (parent.childNodes[i].tagName.indexOf(insertPoint[0]) == -1) + continue; + parent.childNodes[i].appendChild(editor); + inserted = true; + } + if (!inserted) + throw new Error("Could not find the vbox or hbox to insert before!"); + } + } + } + + editor.show(); + + // make sure we make the splitter visible, if needed + if (editor.previousSibling && editor.previousSibling.tagName == "splitter") + editor.previousSibling.show(); +} + +}); diff --git a/client/ext/splitview/splits.js b/client/ext/splitview/splits.js new file mode 100644 index 00000000000..fac6c05b004 --- /dev/null +++ b/client/ext/splitview/splits.js @@ -0,0 +1,322 @@ +/** + * Show a split view; two editors next to each other in one tab + * + * @copyright 2010, Ajax.org B.V. + * @author Mike de Boer + * @license GPLv3 + */ + +define(function(require, exports, module) { + +var Grids = require("ext/splitview/grids"); +var Splits = []; +var EditorClones = {}; +var ActiveClass = "splitview_active"; +var InactiveClass = "splitview_inactive"; +var SplitView, ActiveSplit; + +exports.init = function(splitView) { + SplitView = splitView; + //Grids.init(Grids.DEFAULT_GRID); +}; + +exports.create = function(page, gridLayout, type) { + type = type || null; + + Grids.init(gridLayout); + + //var grid = createGrids(page)[layout]; + var editor = page.$editor.amlEditor; + + var split = { + editors: [editor], + pages: [page], + gridLayout: gridLayout + }; + addEditorListeners.call(this, editor); + Splits.push(split); + + if (type == "clone") { + if (!EditorClones.cloneEditor) { + EditorClones.cloneEditor = editor.cloneNode(true); + EditorClones.cloneEditor.removeAttribute("id"); + apf.document.body.appendChild(EditorClones.cloneEditor); + + addEditorListeners.call(this, EditorClones.cloneEditor); + } + split.editors.push(EditorClones.cloneEditor); + } + + return split; +}; + +exports.show = function(split) { + if (!split || split === ActiveSplit) + return this; + + this.update(split); + Grids.show(split.gridLayout); + + var i, l; + // maintain page button styles + Splits.forEach(function(aSplit) { + if (aSplit === split) + return; + for (i = 0, l = aSplit.pages.length; i < l; ++i) + aSplit.pages[i].$deactivateButton(); + }); + console.log("pages",split.pages.map(function(page){return page.name;})); + for (i = 0, l = split.pages.length; i < l; ++i) + split.pages[i].$activateButton(); + + ActiveSplit = split; + + return this; +}; + +exports.hide = function(split) { + split = split || ActiveSplit; + Grids.hide(split); + if (split === ActiveSplit) + ActiveSplit = null; +}; + +exports.update = function(split, gridLayout) { + split = split || ActiveSplit; + gridLayout = Grids.init(gridLayout || split.gridLayout); + + var page = split.pages[0]; + var amlPage = page.fake ? page.relPage : page; + split.gridLayout = gridLayout; + + // destroy the split view if it contains NOT more than 1 editor. + //console.log("number of pages in split view:", split.pages.length,"vs editors:", + // split.editors.length,page.name,split.pages.map(function(page){return page.name})); + if (split.pages.length === 1) { + var editor = page.$editor.amlEditor; + editor.removeAttribute("model"); + editor.removeAttribute("actiontracker"); + amlPage.appendChild(editor); + editor.show(); + + removeEditorListeners(editor); + removeEditorListeners(split.editors[0]); + clearSplitViewStyles(page); + + Grids.hide(split.gridLayout); + + if (ActiveSplit === split) + ActiveSplit = null; + Splits.remove(split); + console.log("split removed",Splits); + // split removed, use the escape hatch... + return; + } + + // make sure current grid is the only one visible. + Grids.show(gridLayout); + + // sort the editors and pages before being added to the grid + sortEditorsAndPages(split); + //console.log("split editors:", split.editors.length, split.editors.map(function(e) { return e.id; })); + Grids.update(gridLayout, split); + + return this; +}; + +exports.mutate = function(split, page) { + split = split || ActiveSplit; + console.log("mutate called"); + var activePage = tabEditors.getPage(); + var pageIdx = split ? split.pages.indexOf(page) : -1; + var _self = this; + + // Remove an editor from the split view + if (pageIdx > -1) { + // @todo re-instate CLONE view + if (split.clone && split.clone === page) + return this.endCloneView(page); + + var editorIdx = pageIdx; + split.pages.splice(pageIdx, 1); + + var editor = split.editors[editorIdx]; + split.editors.splice(editorIdx, 1); + editor.removeAttribute("model"); + editor.removeAttribute("actiontracker"); + removeEditorListeners(editor); + + // use setTimout to circumvent the APF layout manager to go bonkers + setTimeout(function() { + clearSplitViewStyles(page); + editor.hide(); + if (tabEditors.getPage() !== split.pages[0]) + tabEditors.set(split.pages[0]); + _self.update(split); + }); + } + // Add an editor to the split view + else if (!split || split.editors.length < 3) { + var clones = createEditorClones.call(this, page.$editor.amlEditor); + + if (!split) { + // create the split view, with the currently active tab as first page + // and editor + if (page === activePage) + return true; + + split = this.create(activePage); + var oEditor = activePage.$editor.amlEditor; + oEditor.setAttribute("model", activePage.$model); + oEditor.setAttribute("actiontracker", activePage.$at); + } + + var editorToUse; + for (var i = 0, l = clones.length; i < l; ++i) { + if (split.editors.indexOf(clones[i]) == -1) { + editorToUse = clones[i]; + break; + } + } + if (!editorToUse && split.editors.indexOf(page.$editor.amlEditor) === -1) + editorToUse = page.$editor.amlEditor; + + split.pages.push(page); + split.editors.push(editorToUse); + //console.log("setting model of ", editorToUse.id, "to", page.$model.data.xml); + editorToUse.setAttribute("model", page.$model); + editorToUse.setAttribute("actiontracker", page.$at); + consolidateEditorSession(page, editorToUse); + addEditorListeners.call(_self, editorToUse); + + // use setTimout to circumvent the APF layout manager to go bonkers + setTimeout(function() { + _self.show(split); + }); + } +}; + +exports.get = function(amlNode) { + var nodeName = amlNode.tagName; + var split + var i = 0; + var l = Splits.length; + var splits = []; + + if (nodeName.indexOf("page") > -1) { + for (; i < l; ++i) { + split = Splits[i]; + if (!split || split.pages.indexOf(amlNode) === -1) + continue; + return [split]; + } + } + // search by editor (may yield multiple results) + else { + for (; i < l; ++i) { + split = Splits[i]; + if (!split || split.editors.indexOf(amlNode) === -1) + continue; + splits.push(split); + } + } + return splits; +}; + +exports.is = function(amlNode) { + return !!this.get(amlNode); +}; + +function sortEditorsAndPages(split) { + // lstOpenFiles.$model.data.selectNodes("//file") + var pages = tabEditors.getPages(); + var p = []; + var e = []; + var index; + //console.log("before sort: ", [].concat(split.pages).map(function(p) { return p.name; }), + // [].concat(split.editors).map(function(e) { return e.id; })); + for (var i = 0, c = 0, l = pages.length, l2 = split.pages.length; i < l && c < l2; ++i) { + if ((index = split.pages.indexOf(pages[i])) > -1) { + //console.log("pushing page at index " + i + " which is in the split at " + // + index + ", names " + pages[i].name + ", " + split.pages[index].name); + p.push(split.pages[index]); + e.push(split.editors[index]); + ++c; + } + } + //console.log("after sort:", p.map(function(p) { return p.name; }), e.map(function(e) { return e.id; })); + split.pages = p; + split.editors = e; +} + +function createEditorClones(editor) { + var id = editor.tagName; + if (EditorClones[id] && EditorClones[id].length) + return EditorClones[id]; + + EditorClones[id] = []; + + var editor; + for (var i = 0; i < 2; ++i) { + editor = editor.cloneNode(true); + editor.removeAttribute("id"); + editor.setAttribute("visible", false); + EditorClones[id].push(editor); + apf.document.body.appendChild(editor); + addEditorListeners.call(this, editor); + } + + return EditorClones[id]; +} + +/** + * Add listeners to the editor element, to keep track of the editor's focus. + * Each time the focus changes, the tab title color will highlight. + */ +function addEditorListeners(editor) { + if (editor.$splitListener) + return; + editor.addEventListener("focus", editor.$splitListener = function(e) { + //console.log("got focus?", editor.id); + onEditorFocus(editor); + }); +} + +function removeEditorListeners(editor) { + if (!editor.$splitListener) + return; + editor.removeEventListener("focus", editor.$splitListener); + delete editor.$splitListener; +} + +function onEditorFocus(editor) { + var splits = exports.get(editor); + + splits.forEach(function(split) { + var activePage = split.pages[split.editors.indexOf(editor)]; + split.pages.forEach(function(page) { + if (page === activePage) + apf.setStyleClass(page.$button, ActiveClass, [InactiveClass]); + else + apf.setStyleClass(page.$button, InactiveClass, [ActiveClass]); + }); + }); +} + +function consolidateEditorSession(page, editor) { + var session = SplitView.getEditorSession(page); + if (!session && page.$editor.setDocument) + page.$editor.setDocument(page.$doc, page.$at); + session = SplitView.getEditorSession(page) + if (editor.value !== session) + editor.setProperty("value", session); +} + +function clearSplitViewStyles(splitOrPage) { + var pages = (typeof splitOrPage.tagName != "undefined") ? [splitOrPage] : split.pages; + pages.forEach(function(page) { + apf.setStyleClass(page.$button, null, [ActiveClass, InactiveClass]); + }); +} + +}); \ No newline at end of file diff --git a/client/ext/splitview/splitview.js b/client/ext/splitview/splitview.js index 9575119a109..860c4c3c32d 100644 --- a/client/ext/splitview/splitview.js +++ b/client/ext/splitview/splitview.js @@ -2,6 +2,7 @@ * Show a split view; two editors next to each other in one tab * * @copyright 2010, Ajax.org B.V. + * @author Mike de Boer * @license GPLv3 */ @@ -10,336 +11,15 @@ define(function(require, exports, module) { var ide = require("core/ide"); var ext = require("core/ext"); var css = require("text!ext/splitview/splitview.css"); -var Code = require("ext/code/code"); var Tabbehaviors = require("ext/tabbehaviors/tabbehaviors"); var Editors = require("ext/editors/editors"); -var EditSession = require("ace/edit_session").EditSession; - -var Layouts = [ - null, -"\ -+--+--+--+\n\ -| | | |\n\ -+--+--+--+", -// vbox -// --> hbox -// --> splitter -// --> splitter - -"\ -+-----+\n\ -| |\n\ -+-----+\n\ -| |\n\ -+-----+\n\ -| |\n\ -+-----+", -// vbox -// --> splitter -// --> splitter - -"\ -+--+--+\n\ -| | |\n\ -| +--+\n\ -| | |\n\ -+--+--+", -// vbox -// --> hbox -// --> splitter -// --> vbox -// --> splitter - -"\ -+--+--+\n\ -| | |\n\ -+--+ |\n\ -| | |\n\ -+--+--+", -// vbox -// --> hbox -// --> vbox -// --> splitter -// --> splitter - -"\ -+--+--+\n\ -| | |\n\ -+--+--+\n\ -| |\n\ -+-----+", -// vbox -// --> hbox -// --> splitter -// --> splitter +var Splits = require("ext/splitview/splits"); -"\ -+-----+\n\ -| |\n\ -+--+--+\n\ -| | |\n\ -+--+--+" -// vbox -// --> splitter -// --> hbox -// --> splitter -]; - -var Grids = [null]; - -var vbox = "vbox"; -var hbox = "hbox"; -var activeClass = "splitview_active"; -var inactiveClass = "splitview_inactive"; +var EditSession = require("ace/edit_session").EditSession; var mnuCloneView, mnuSplitAlign; -function createGrids(page) { - var amlPage = page.fake ? page.relPage : page; - if (amlPage.$grids) - return amlPage.$grids; - - amlPage.$grids = [null]; - // first: - var grid = amlPage.$grids[1] = { - main: amlPage.appendChild(new apf.vbox({anchors: "0 0 0 0"})) - }; - grid.single = grid.main.appendChild(new apf.hbox({flex: 1, padding: 0})); - grid.double = grid.single; - grid.splitters = []; - - // second: - grid = amlPage.$grids[2] = { - main: amlPage.appendChild(new apf.vbox({anchors: "0 0 0 0"})) - }; - grid.single = grid.double = grid.main; - grid.splitters = []; - - return amlPage.$grids; -} - -function createSplitView(page, layout, type) { - layout = layout || 1; - type = type || null; - var grid = createGrids(page)[layout]; - var editor = page.$editor.amlEditor; - - var split = { - editors: [editor], - pages: [page], - grid: grid - }; - addEditorListeners.call(this, editor); - this.splits.push(split); - - if (type == "clone") { - if (!this.$cloneEditor) { - var markup = "" - + getEditorMarkup(editor).replace(/id="[\w]*"/, "id=\"ceCloneEditor\"") - + ""; - apf.document.body.insertMarkup(markup); - - this.$cloneEditor = ceCloneEditor; - addEditorListeners.call(this, this.$cloneEditor); - } - split.editors.push(this.$cloneEditor); - } - - return split; -} - -function sortEditorsAndPages(split) { - // lstOpenFiles.$model.data.selectNodes("//file") - var pages = tabEditors.getPages(); - var p = []; - var e = []; - var index; - //console.log("before sort: ", [].concat(split.pages).map(function(p) { return p.name; }), - // [].concat(split.editors).map(function(e) { return e.id; })); - for (var i = 0, c = 0, l = pages.length, l2 = split.pages.length; i < l && c < l2; ++i) { - if ((index = split.pages.indexOf(pages[i])) > -1) { - //console.log("pushing page at index " + i + " which is in the split at " - // + index + ", names " + pages[i].name + ", " + split.pages[index].name); - p.push(split.pages[index]); - e.push(split.editors[index]); - ++c; - } - } - //console.log("after sort:", p.map(function(p) { return p.name; }), e.map(function(e) { return e.id; })); - split.pages = p; - split.editors = e; -} - -function hideGrids(amlPage, exception) { - // make sure all the editors are kept safe: - for (var name in EditorClones) { - EditorClones[name].forEach(function(editor) { - //console.log("hiding editor:", editor.id); - apf.document.body.appendChild(editor); - editor.hide(); - }); - } - - amlPage.$grids.forEach(function(g) { - if (!g) - return; - - var vis = (g === exception); - - // remove all splitters first - //console.log("splitters:", g.single.getElementsByTagName("a:splitter").length, "vs", g.splitters.length) - for (i = g.splitters.length - 1; i >= 0; --i) { - if (g.splitters[i].parentNode) { - //console.log("removing splitter:",g.splitters[i]); - g.splitters[i].parentNode.removeChild(g.splitters[i]); - } - else { - //console.log("removing splitter without parentNode:",g.splitters[i]); - g.splitters[i].destroy(); - } - g.splitters.splice(i, 1); - } - - g.main.setAttribute("visible", vis); - }); -} - -function updateSplitViewGrid(split, layout) { - layout = layout || split.layout || 1; - - var editor, splitter, i, l; - var page = split.pages[0]; - - var grid = split.grid = createGrids(page)[layout]; - var amlPage = page.fake ? page.relPage : page; - split.layout = layout; - - // destroy the split view if it contains NOT more than 1 editor. - //console.log("number of pages in split view:", split.pages.length,"vs editors:", - // split.editors.length,page.name,split.pages.map(function(page){return page.name})); - if (split.pages.length === 1) { - editor = page.$editor.amlEditor; - editor.removeAttribute("model"); - editor.removeAttribute("actiontracker"); - amlPage.appendChild(editor); - editor.show(); - removeEditorListeners(editor); - removeEditorListeners(split.editors[0]); - clearSplitViewStyles(page); - - hideGrids(amlPage); - - for (i = this.splits.length - 1; i >= 0; --i) { - if (this.splits[i] !== split) - continue; - this.splits.splice(i, 1); - break; - } - // split removed, use the escape hatch... - return; - } - - // make sure current grid is the only one visible. - hideGrids(amlPage, grid); - - // sort the editors and pages before being added to the grid - sortEditorsAndPages(split); - //console.log("split editors:", split.editors.length, split.editors.map(function(e) { return e.id; })); - for (i = 0, l = split.editors.length; i < l; ++i) { - editor = split.editors[i]; - if (i === 0) { - grid.single.appendChild(editor); - grid.splitters.push( - grid.single.appendChild(new apf.splitter({ skin: "darksplitter" })) - ); - } - else { - if (i === 2) { - grid.splitters.push( - grid.double.appendChild(new apf.splitter({ skin: "darksplitter" })) - ); - } - grid.double.appendChild(editor); - } - // make sure it's visible! - editor.show(); - } - //console.log("updating end."); -} - -var EditorClones = {}; - -function getEditorMarkup(editor) { - if (!editor.$aml || !editor.$aml.xml || editor.$aml.xml.indexOf(""] - var editorMarkup = getEditorMarkup(editor); - var _self = this; - - for (var i = 0; i < 2; ++i) { - markup.push(editorMarkup.replace(/id="[\w]*"/, "id=\"" + id + i + "\"")); - ids.push(id + i); - } - - apf.document.body.insertMarkup(markup.join("") + ""); - ids.forEach(function(eid) { - var o = self[eid]; - EditorClones[id].push(o); - apf.document.body.appendChild(o); - addEditorListeners.call(_self, o); - }); - return EditorClones[id]; -} - -function addEditorListeners(editor) { - if (editor.$splitListener) - return; - var _self = this; - editor.addEventListener("focus", editor.$splitListener = function(e) { - //console.log("got focus?", editor.id); - _self.onEditorFocus(editor); - }); -} - -function removeEditorListeners(editor) { - if (!editor.$splitListener) - return; - editor.removeEventListener("focus", editor.$splitListener); - delete editor.$splitListener; -} - -function getEditorSession(page) { - var doc = page.$doc; - return doc.acesession || doc.session || null; -} - -function consolidateEditorSession(page, editor) { - var session = getEditorSession(page); - if (!session && page.$editor.setDocument) - page.$editor.setDocument(page.$doc, page.$at); - session = getEditorSession(page) - if (editor.value !== session) - editor.setProperty("value", session); -} - -function clearSplitViewStyles(splitOrPage) { - var pages = (typeof splitOrPage.tagName != "undefined") ? [splitOrPage] : split.pages; - pages.forEach(function(page) { - apf.setStyleClass(page.$button, null, [activeClass, inactiveClass]); - }); -} - module.exports = ext.register("ext/splitview/splitview", { dev : "Ajax.org", alone : true, @@ -381,7 +61,7 @@ module.exports = ext.register("ext/splitview/splitview", { type : "check", checked : true, onclick : function() { - _self.changeLayout(tabEditors.contextPage, this.checked ? 1 : 2); + _self.changeLayout(tabEditors.contextPage, this.checked ? "3rows" : "3cols"); } })) ) @@ -405,12 +85,13 @@ module.exports = ext.register("ext/splitview/splitview", { tabEditors.addEventListener("tabselectmouseup", function(e) { var page = this.$activepage; - var split = _self.getSplitViewByPage(page); + var split = Splits.get(page); - if (split) { - updateSplitViewGrid(split); - } + if (split && split.length) + Splits.update(split[0]); }); + + Splits.init(this); }, mergetableft: function() { @@ -422,20 +103,22 @@ module.exports = ext.register("ext/splitview/splitview", { }, mergeTab: function(dir) { - var bRight = dir == "right", - tabs = tabEditors, - pages = tabs.getPages(), - curr = tabs.getPage(), - currIdx = pages.indexOf(curr); + var bRight = dir == "right"; + var tabs = tabEditors; + var pages = tabs.getPages(); + var curr = tabs.getPage(); + var currIdx = pages.indexOf(curr); if (!curr || pages.length == 1) return; + var idx = currIdx + (bRight ? 1 : -1); if (idx < 0) idx = pages.length - 1; if (idx > pages.length -1) idx = 0; - this.mutateSplitView(pages[idx]); + // pass in null to mutate the active split view + Splits.mutate(null, pages[idx]); return false; }, @@ -446,10 +129,9 @@ module.exports = ext.register("ext/splitview/splitview", { */ onFileClose: function(e) { var page = e.page; - - if (this.isSplitViewPage(page)) { - this.mutateSplitView(page); - } + var split = Splits.get(page); + if (split.length) + Splits.mutate(split[0], page); }, /** @@ -461,13 +143,14 @@ module.exports = ext.register("ext/splitview/splitview", { onTabClick: function(e) { var page = e.page; var shiftKey = e.htmlEvent.shiftKey; + var split = Splits.get(page); + split = split.length ? split[0] : null - if (this.isSplitViewPage(page) && !shiftKey) { - var split = this.getSplitViewByPage(page); + if (split && !shiftKey) { var activePage = tabEditors.getPage(); var ret = null; - for (i = 0, l = split.pages.length; i < l; ++i) { + for (var i = 0, l = split.pages.length; i < l; ++i) { if (split.pages[i] !== activePage) continue; ret = false; @@ -481,9 +164,8 @@ module.exports = ext.register("ext/splitview/splitview", { ret = false; } - if (!shiftKey) { + if (!shiftKey) return true; - } return ret; } @@ -493,7 +175,7 @@ module.exports = ext.register("ext/splitview/splitview", { // tabs can be merged into and unmerged from a splitview by clicking a // tab while holding shift - return !this.mutateSplitView(page) + return !Splits.mutate(split, page); }, /** @@ -508,10 +190,11 @@ module.exports = ext.register("ext/splitview/splitview", { var dir = e.dir; var pages = e.pages; - var split = this.getSplitViewByPage(pages[idx]); + var split = Splits.get(pages[idx]); //console.log("cycletab split?", split, pages[idx], e); - if (!split) + if (!split.length) return; + split = split[0]; var start = split.pages.indexOf(pages[idx]); @@ -524,7 +207,7 @@ module.exports = ext.register("ext/splitview/splitview", { } //console.log("tab is cycling...", dir); - var count = 0 + var count = 0; var max = pages.length + 2; if (dir == "right") { if (start === 0) @@ -547,169 +230,28 @@ module.exports = ext.register("ext/splitview/splitview", { e.returnValue = correct(idx); }, - onEditorFocus: function(editor) { - var splits = this.getSplitViewsByEditor(editor); - - splits.forEach(function(split) { - var activePage = split.pages[split.editors.indexOf(editor)]; - split.pages.forEach(function(page) { - if (page === activePage) - apf.setStyleClass(page.$button, activeClass, [inactiveClass]); - else - apf.setStyleClass(page.$button, inactiveClass, [activeClass]); - }); - }); - }, - - /** - * - */ - startCloneView: function(page) { - var view = this.getCloneView(page); - var doc = page.$doc; - - if (view || !doc || !getEditorSession(page)) - return; - - var view = createSplitView.call(this, page, 1, "clone"); - var editor = view.editors[1]; - - fake = tabEditors.add("{([@changed] == 1 ? '*' : '') + [@name]}", page.$model.data.getAttribute("path") - + "_clone", page.$editor.path, page.nextSibling || null, function(newPage){ - newPage.contentType = page.contentType; - newPage.$at = page.$at; - newPage.$doc = doc; - newPage.$editor = editor; - newPage.setAttribute("tooltip", "[@path]"); - newPage.setAttribute("class", - "{parseInt([@saving], 10) || parseInt([@lookup], 10) ? (tabEditors.getPage(tabEditors.activepage) == this ? 'saving_active' : 'saving') : \ - ([@loading] ? (tabEditors.getPage(tabEditors.activepage) == this ? 'loading_active' : 'loading') : '')}" - ); - newPage.setAttribute("model", newPage.$model = page.$model); - - view.pages.push(newPage); - view.clone = newPage; - - newPage.$activateButton(); - }); - - page.addEventListener("DOMNodeRemovedFromDocument", function(e) { - if (typeof tabEditors == "undefined") - return; - tabEditors.remove(fake); - }); - - Editors.initEditorEvents(fake, page.$model); - - this.updateSplitView(null, page); - }, - - mutateSplitView: function(page) { - var i, l; - - var activePage = tabEditors.getPage(); - var activeSplit = this.getSplitViewByPage(activePage); - var pageIdx = activeSplit ? activeSplit.pages.indexOf(page) : -1; - var _self = this; - - // Remove an editor from the split view - if (pageIdx > -1) { - if (activeSplit.clone && activeSplit.clone === page) - return this.endCloneView(page); - - var editorIdx = pageIdx; - activeSplit.pages.splice(pageIdx, 1); - editor = activeSplit.editors[editorIdx]; - activeSplit.editors.splice(editorIdx, 1); - editor.removeAttribute("model"); - editor.removeAttribute("actiontracker"); - removeEditorListeners(editor); - - // use setTimout to circumvent the APF layout manager to go bonkers - setTimeout(function() { - page.$deactivateButton(); - - clearSplitViewStyles(page); - editor.hide(); - if (tabEditors.getPage() !== activeSplit.pages[0]) - tabEditors.set(activeSplit.pages[0]); - //updateSplitViewGrid.call(_self, activeSplit); - _self.updateSplitView(null, activeSplit.pages[0]); - }); - } - // Add an editor to the split view - else if (!activeSplit || activeSplit.editors.length < 3) { - var editors = createEditorClones.call(this, page.$editor.amlEditor); - var editor = null; - - if (!activeSplit) { - if (page === activePage) - return true; - - // check whether 'page' is already part of a split view - var pageToBeJoinedInSplitView = this.getSplitViewByPage(page); - if (pageToBeJoinedInSplitView) { - // if active, then find that split and remove it - this.splits.splice(this.splits.indexOf(this.getSplitViewByPage(page))) - } - - activeSplit = createSplitView.call(this, activePage); - var oEditor = activePage.$editor.amlEditor; - oEditor.setAttribute("model", activePage.$model); - oEditor.setAttribute("actiontracker", activePage.$at); - } - for (i = 0, l = editors.length; i < l; ++i) { - if (activeSplit.editors.indexOf(editors[i]) === -1) { - editor = editors[i]; - break; - } - } - if (!editor && activeSplit.editors.indexOf(page.$editor.amlEditor) === -1) - editor = page.$editor.amlEditor; - - activeSplit.pages.push(page); - activeSplit.editors.push(editor); - //console.log("setting model of ", editor.id, "to", page.$model.data.xml); - editor.setAttribute("model", page.$model); - editor.setAttribute("actiontracker", page.$at); - consolidateEditorSession(page, editor); - addEditorListeners.call(_self, editor); - - // use setTimout to circumvent the APF layout manager to go bonkers - setTimeout(function() { - editor.show(); - _self.updateSplitView(null, page); - page.$activateButton(); - }); - } - - return true; - }, - updateSplitView: function(previous, next) { - var i, l; var doc = next.$doc; var at = next.$at; // check if this is a valid clone session - var split = this.getSplitViewByPage(next); + var split = Splits.get(next); + split = split.length ? split[0] : null; + console.log("got split?",split); // hide the previous clone session (if any) if (previous && previous.$model) { - var oldSplit = this.getSplitViewByPage(previous); - if (oldSplit) { - // start at one, the first page is already deactivated. - for (i = 0, l = oldSplit.pages.length; i < l; ++i) - oldSplit.pages[i].$deactivateButton(); - + var oldSplit = Splits.get(previous); + oldSplit = oldSplit.length ? oldSplit[0] : null; + console.log("got old split?",oldSplit); + if (oldSplit && !split) { // the next page is NOT a split view - if (!split) { - var oldPage = previous.fake ? previous.relPage : previous; - var nextPage = next.fake ? next.relPage : next; - //oldPage.appendChild(nextPage.firstChild); - hideGrids(oldPage); - nextPage.appendChild(next.$editor.amlEditor); - next.$editor.amlEditor.show(); - } + //for (var i = 0, l = oldSplit.pages.length; i < l; ++i) + // oldSplit.pages[i].$deactivateButton(); + + Splits.hide(oldSplit); + + var nextPage = next.fake ? next.relPage : next; + nextPage.appendChild(next.$editor.amlEditor); } } @@ -722,15 +264,15 @@ module.exports = ext.register("ext/splitview/splitview", { return; } - updateSplitViewGrid.call(this, split); - mnuSplitAlign.setAttribute("checked", split.layout === 1); + Splits.show(split); + mnuSplitAlign.setAttribute("checked", split.gridLayout == "3rows"); + var _self = this; split.pages.forEach(function(page, idx) { - page.$activateButton(); var editor = split.editors[idx]; if (!editor) return; - var session = getEditorSession(page); + var session = _self.getEditorSession(page); //console.log("switch: ", session); if (editor.value !== session) editor.setProperty("value", session); @@ -766,12 +308,55 @@ module.exports = ext.register("ext/splitview/splitview", { apf.layout.forceResize(); }, - changeLayout: function(page, layout) { - var split = this.getSplitViewByPage(page); - if (!split || split.layout === layout) + changeLayout: function(page, gridLayout) { + var split = Splits.get(page); + if (!split.length || split.gridLayout == gridLayout) + return; + + Splits.update(split[0], gridLayout); + }, + + /** + * + */ + startCloneView: function(page) { + var view = this.getCloneView(page); + var doc = page.$doc; + + if (view || !doc || !this.getEditorSession(page)) return; - updateSplitViewGrid.call(this, split, layout); + var view = createSplitView.call(this, page, 1, "clone"); + var editor = view.editors[1]; + + var fake = tabEditors.add("{([@changed] == 1 ? '*' : '') + [@name]}", page.$model.data.getAttribute("path") + + "_clone", page.$editor.path, page.nextSibling || null, function(newPage){ + newPage.contentType = page.contentType; + newPage.$at = page.$at; + newPage.$doc = doc; + newPage.$editor = editor; + newPage.setAttribute("tooltip", "[@path]"); + newPage.setAttribute("class", + "{parseInt([@saving], 10) || parseInt([@lookup], 10) ? (tabEditors.getPage(tabEditors.activepage) == this ? 'saving_active' : 'saving') : \ + ([@loading] ? (tabEditors.getPage(tabEditors.activepage) == this ? 'loading_active' : 'loading') : '')}" + ); + newPage.setAttribute("model", newPage.$model = page.$model); + + view.pages.push(newPage); + view.clone = newPage; + + newPage.$activateButton(); + }); + + page.addEventListener("DOMNodeRemovedFromDocument", function(e) { + if (typeof tabEditors == "undefined") + return; + tabEditors.remove(fake); + }); + + Editors.initEditorEvents(fake, page.$model); + + this.updateSplitView(null, page); }, endCloneView: function(page) { @@ -810,33 +395,9 @@ module.exports = ext.register("ext/splitview/splitview", { return null; }, - getSplitViewByPage: function(page) { - var split, i, l; - for (var i = 0, l = this.splits.length; i < l; ++i) { - split = this.splits[i]; - if (!split || split.pages.indexOf(page) === -1) - continue; - return split; - } - - return null; - }, - - getSplitViewsByEditor: function(editor) { - var split, i, l; - var splits = []; - for (var i = 0, l = this.splits.length; i < l; ++i) { - split = this.splits[i]; - if (!split || split.editors.indexOf(editor) === -1) - continue; - splits.push(split); - } - - return splits; - }, - - isSplitViewPage: function(page) { - return !!this.getSplitViewByPage(page); + getEditorSession: function(page) { + var doc = page.$doc; + return doc.acesession || doc.session || null; }, enable : function(){ diff --git a/server/cloud9/ide.js b/server/cloud9/ide.js index 1c55ea3148a..3d651f3bd36 100644 --- a/server/cloud9/ide.js +++ b/server/cloud9/ide.js @@ -127,7 +127,8 @@ Ide.DEFAULT_PLUGINS = [ "ext/codecomplete/codecomplete", "ext/vim/vim", "ext/jslanguage/jslanguage", - "ext/autotest/autotest" + "ext/autotest/autotest", + "ext/splitview/splitview" //"ext/acebugs/acebugs" ]; From 89ae7b1b9ef3ed138bdfdcf81aa89861431af871 Mon Sep 17 00:00:00 2001 From: mikedeboer Date: Fri, 9 Dec 2011 11:37:02 +0100 Subject: [PATCH 02/69] additional improvements, splitview more stable, ready for clone-view --- client/ext/splitview/grids.js | 29 +++-------------------------- client/ext/splitview/splits.js | 31 +++++++++++++++++++++++++------ client/ext/splitview/splitview.js | 8 -------- 3 files changed, 28 insertions(+), 40 deletions(-) diff --git a/client/ext/splitview/grids.js b/client/ext/splitview/grids.js index e988dc2a4a6..f63de0ee05e 100644 --- a/client/ext/splitview/grids.js +++ b/client/ext/splitview/grids.js @@ -14,9 +14,6 @@ var GridLayouts = { "+--+--+--+\n" + "| | | |\n" + "+--+--+--+", - // hbox - // --> splitter - // --> splitter "struct": { "hbox": [ "splitter", @@ -38,9 +35,6 @@ var GridLayouts = { "+-----+\n" + "| |\n" + "+-----+", - // vbox - // --> splitter - // --> splitter "struct": { "vbox": [ "splitter", @@ -60,10 +54,6 @@ var GridLayouts = { "| +--+\n" + "| | |\n" + "+--+--+", - // --> hbox - // --> splitter - // --> vbox - // --> splitter "struct": { "hbox": { "splitter": 1, @@ -85,10 +75,6 @@ var GridLayouts = { "+--+ |\n" + "| | |\n" + "+--+--+", - // --> hbox - // --> vbox - // --> splitter - // --> splitter "struct": { "hbox": { "vbox": { @@ -110,10 +96,6 @@ var GridLayouts = { "+--+--+\n" + "| |\n" + "+-----+", - // vbox - // --> hbox - // --> splitter - // --> splitter "struct": { "vbox": { "hbox": { @@ -135,10 +117,6 @@ var GridLayouts = { "+--+--+\n" + "| | |\n" + "+--+--+", - // vbox - // --> splitter - // --> hbox - // --> splitter "struct": { "vbox": { "splitter": 1, @@ -190,10 +168,9 @@ exports.update = function(gridLayout, split) { insertEditorAt(grid.node, split.editors[i], [].concat(grid.insertPoints[i])); // hide splitters that we don't need to see anymore - if (!grid.splitters) - grid.splitters = grid.node.selectNodes("splitter"); - for (i = grid.splitters.length - 1; i >= l - 1; --i) - grid.splitters[i].hide(); + var splitters = grid.node.selectNodes("splitter"); + for (i = splitters.length - 1; i >= l - 1; --i) + splitters[i].hide(); }; exports.show = function(gridLayout) { diff --git a/client/ext/splitview/splits.js b/client/ext/splitview/splits.js index fac6c05b004..f579c22f7f2 100644 --- a/client/ext/splitview/splits.js +++ b/client/ext/splitview/splits.js @@ -25,7 +25,6 @@ exports.create = function(page, gridLayout, type) { Grids.init(gridLayout); - //var grid = createGrids(page)[layout]; var editor = page.$editor.amlEditor; var split = { @@ -68,6 +67,8 @@ exports.show = function(split) { console.log("pages",split.pages.map(function(page){return page.name;})); for (i = 0, l = split.pages.length; i < l; ++i) split.pages[i].$activateButton(); + for (i = 0, l = split.editors.length; i < l; ++i) + split.editors[i].show(); ActiveSplit = split; @@ -77,6 +78,11 @@ exports.show = function(split) { exports.hide = function(split) { split = split || ActiveSplit; Grids.hide(split); + var i, l; + for (i = 0, l = split.pages.length; i < l; ++i) + split.pages[i].$deactivateButton(); + for (i = 0, l = split.editors.length; i < l; ++i) + split.editors[i].hide(); if (split === ActiveSplit) ActiveSplit = null; }; @@ -99,6 +105,14 @@ exports.update = function(split, gridLayout) { amlPage.appendChild(editor); editor.show(); + if (EditorClones[editor.tagName]) { + for (var clone, i = 0, l = EditorClones[editor.tagName].length; i < l; ++i) { + clone = EditorClones[editor.tagName][i]; + clone.hide(); + apf.document.body.appendChild(clone); + } + } + removeEditorListeners(editor); removeEditorListeners(split.editors[0]); clearSplitViewStyles(page); @@ -126,7 +140,6 @@ exports.update = function(split, gridLayout) { exports.mutate = function(split, page) { split = split || ActiveSplit; - console.log("mutate called"); var activePage = tabEditors.getPage(); var pageIdx = split ? split.pages.indexOf(page) : -1; var _self = this; @@ -145,15 +158,18 @@ exports.mutate = function(split, page) { editor.removeAttribute("model"); editor.removeAttribute("actiontracker"); removeEditorListeners(editor); + editor.hide(); + apf.document.body.appendChild(editor); // use setTimout to circumvent the APF layout manager to go bonkers - setTimeout(function() { + //setTimeout(function() { + page.$deactivateButton(); clearSplitViewStyles(page); editor.hide(); if (tabEditors.getPage() !== split.pages[0]) tabEditors.set(split.pages[0]); _self.update(split); - }); + //}); } // Add an editor to the split view else if (!split || split.editors.length < 3) { @@ -182,6 +198,7 @@ exports.mutate = function(split, page) { editorToUse = page.$editor.amlEditor; split.pages.push(page); + page.$activateButton(); split.editors.push(editorToUse); //console.log("setting model of ", editorToUse.id, "to", page.$model.data.xml); editorToUse.setAttribute("model", page.$model); @@ -190,10 +207,12 @@ exports.mutate = function(split, page) { addEditorListeners.call(_self, editorToUse); // use setTimout to circumvent the APF layout manager to go bonkers - setTimeout(function() { + //setTimeout(function() { _self.show(split); - }); + //}); } + + return true; }; exports.get = function(amlNode) { diff --git a/client/ext/splitview/splitview.js b/client/ext/splitview/splitview.js index 860c4c3c32d..1684ad2ada4 100644 --- a/client/ext/splitview/splitview.js +++ b/client/ext/splitview/splitview.js @@ -145,7 +145,6 @@ module.exports = ext.register("ext/splitview/splitview", { var shiftKey = e.htmlEvent.shiftKey; var split = Splits.get(page); split = split.length ? split[0] : null - if (split && !shiftKey) { var activePage = tabEditors.getPage(); var ret = null; @@ -164,9 +163,6 @@ module.exports = ext.register("ext/splitview/splitview", { ret = false; } - if (!shiftKey) - return true; - return ret; } @@ -244,10 +240,6 @@ module.exports = ext.register("ext/splitview/splitview", { oldSplit = oldSplit.length ? oldSplit[0] : null; console.log("got old split?",oldSplit); if (oldSplit && !split) { - // the next page is NOT a split view - //for (var i = 0, l = oldSplit.pages.length; i < l; ++i) - // oldSplit.pages[i].$deactivateButton(); - Splits.hide(oldSplit); var nextPage = next.fake ? next.relPage : next; From 6523c5c02b4f420960721f525c03a264f2909ca1 Mon Sep 17 00:00:00 2001 From: mikedeboer Date: Mon, 12 Dec 2011 16:24:09 +0100 Subject: [PATCH 03/69] added clone view back and splits are now saved and restored from settings --- client/ext/splitview/grids.js | 12 +- client/ext/splitview/splits.js | 141 +++++++++-------- client/ext/splitview/splitview.js | 254 +++++++++++++++++++++--------- 3 files changed, 259 insertions(+), 148 deletions(-) diff --git a/client/ext/splitview/grids.js b/client/ext/splitview/grids.js index f63de0ee05e..1158ce85eba 100644 --- a/client/ext/splitview/grids.js +++ b/client/ext/splitview/grids.js @@ -142,7 +142,7 @@ var defaultGrid = exports.DEFAULT_GRID = "3cols"; */ exports.init = function(gridLayout) { gridLayout = gridLayout || defaultGrid; - console.log("init called", gridLayout); + //console.log("init called", gridLayout); createGridNodes(gridLayout); return gridLayout; }; @@ -154,14 +154,14 @@ exports.get = function(name) { exports.update = function(gridLayout, split) { var grid = GridLayouts[gridLayout]; - console.log("update: ", split.pages[0].$pHtmlNode, grid.node, grid.node.parentNode, grid.node.$pHtmlNode); + //console.log("update: ", split.pages[0].$pHtmlNode, grid.node, grid.node.parentNode, grid.node.$pHtmlNode); grid.node.show(); // attach the grid layout to the first page of the splitview... var page = split.pages[0]; var amlPage = page.fake ? page.relPage : page; amlPage.appendChild(grid.node); - console.log(split.editors.map(function(editor){return editor.$ext;}), grid.insertPoints); + //console.log(split.editors.map(function(editor){return editor.$ext;}), grid.insertPoints); var i = 0; var l = split.editors.length; for (; i < l; ++i) @@ -224,8 +224,8 @@ function createNodes(struct, splitters, parent) { } var node = parent.appendChild(new apf[nodeName](options)); - if (nodeName == "splitter"){console.log("GOT SPLITTER"); - splitters.push(node);} + if (nodeName == "splitter") + splitters.push(node); // if we just appended the main node to the document, set it as the grid's // main node if (parent === apf.document.body) @@ -275,7 +275,7 @@ function insertEditorAt(parent, editor, insertPoint) { for (var i = 0, l = parent.childNodes.length; i < l; ++i) { if (parent.childNodes[i].tagName.indexOf(insertPoint[0]) == -1) continue; - console.log("setting parent to...",parent.childNodes[i]); + //console.log("setting parent to...",parent.childNodes[i]); parent = parent.childNodes[i]; break; } diff --git a/client/ext/splitview/splits.js b/client/ext/splitview/splits.js index f579c22f7f2..2cca8bca15f 100644 --- a/client/ext/splitview/splits.js +++ b/client/ext/splitview/splits.js @@ -17,34 +17,23 @@ var SplitView, ActiveSplit; exports.init = function(splitView) { SplitView = splitView; - //Grids.init(Grids.DEFAULT_GRID); + return this; }; -exports.create = function(page, gridLayout, type) { - type = type || null; - - Grids.init(gridLayout); +exports.create = function(page, gridLayout) { + gridLayout = Grids.init(gridLayout); var editor = page.$editor.amlEditor; + editor.setAttribute("model", page.$model); + editor.setAttribute("actiontracker", page.$at); + consolidateEditorSession(page, editor); var split = { editors: [editor], pages: [page], gridLayout: gridLayout }; - addEditorListeners.call(this, editor); Splits.push(split); - - if (type == "clone") { - if (!EditorClones.cloneEditor) { - EditorClones.cloneEditor = editor.cloneNode(true); - EditorClones.cloneEditor.removeAttribute("id"); - apf.document.body.appendChild(EditorClones.cloneEditor); - - addEditorListeners.call(this, EditorClones.cloneEditor); - } - split.editors.push(EditorClones.cloneEditor); - } return split; }; @@ -54,6 +43,8 @@ exports.show = function(split) { return this; this.update(split); + if (ActiveSplit) + this.hide(ActiveSplit); Grids.show(split.gridLayout); var i, l; @@ -64,7 +55,7 @@ exports.show = function(split) { for (i = 0, l = aSplit.pages.length; i < l; ++i) aSplit.pages[i].$deactivateButton(); }); - console.log("pages",split.pages.map(function(page){return page.name;})); + //console.log("pages",split.pages.map(function(page){return page.name;})); for (i = 0, l = split.pages.length; i < l; ++i) split.pages[i].$activateButton(); for (i = 0, l = split.editors.length; i < l; ++i) @@ -85,12 +76,14 @@ exports.hide = function(split) { split.editors[i].hide(); if (split === ActiveSplit) ActiveSplit = null; + + return this; }; exports.update = function(split, gridLayout) { split = split || ActiveSplit; gridLayout = Grids.init(gridLayout || split.gridLayout); - + var page = split.pages[0]; var amlPage = page.fake ? page.relPage : page; split.gridLayout = gridLayout; @@ -122,9 +115,9 @@ exports.update = function(split, gridLayout) { if (ActiveSplit === split) ActiveSplit = null; Splits.remove(split); - console.log("split removed",Splits); + //console.log("split removed",Splits); // split removed, use the escape hatch... - return; + return this; } // make sure current grid is the only one visible. @@ -135,20 +128,24 @@ exports.update = function(split, gridLayout) { //console.log("split editors:", split.editors.length, split.editors.map(function(e) { return e.id; })); Grids.update(gridLayout, split); + // make sure the buttons of the pages in the active split are highlighted + if (split === ActiveSplit) { + for (var i = 0, l = split.pages.length; i < l; ++i) + split.pages[i].$activateButton(); + } + return this; }; -exports.mutate = function(split, page) { - split = split || ActiveSplit; +exports.mutate = function(split, page, noShow) { + split = split || split === null ? ActiveSplit : null; var activePage = tabEditors.getPage(); var pageIdx = split ? split.pages.indexOf(page) : -1; - var _self = this; // Remove an editor from the split view if (pageIdx > -1) { - // @todo re-instate CLONE view if (split.clone && split.clone === page) - return this.endCloneView(page); + SplitView.endCloneView(page); var editorIdx = pageIdx; split.pages.splice(pageIdx, 1); @@ -161,15 +158,14 @@ exports.mutate = function(split, page) { editor.hide(); apf.document.body.appendChild(editor); - // use setTimout to circumvent the APF layout manager to go bonkers - //setTimeout(function() { - page.$deactivateButton(); - clearSplitViewStyles(page); - editor.hide(); - if (tabEditors.getPage() !== split.pages[0]) - tabEditors.set(split.pages[0]); - _self.update(split); - //}); + page.$deactivateButton(); + clearSplitViewStyles(page); + editor.hide(); + if (tabEditors.getPage() !== split.pages[0]) + tabEditors.set(split.pages[0]); + + if (!noShow) + this.update(split); } // Add an editor to the split view else if (!split || split.editors.length < 3) { @@ -182,63 +178,56 @@ exports.mutate = function(split, page) { return true; split = this.create(activePage); - var oEditor = activePage.$editor.amlEditor; - oEditor.setAttribute("model", activePage.$model); - oEditor.setAttribute("actiontracker", activePage.$at); } var editorToUse; - for (var i = 0, l = clones.length; i < l; ++i) { - if (split.editors.indexOf(clones[i]) == -1) { - editorToUse = clones[i]; - break; + if (split.clone && page === split.clone) { + editorToUse = EditorClones.cloneEditor; + } + else { + for (var i = 0, l = clones.length; i < l; ++i) { + if (split.editors.indexOf(clones[i]) == -1) { + editorToUse = clones[i]; + break; + } } } if (!editorToUse && split.editors.indexOf(page.$editor.amlEditor) === -1) editorToUse = page.$editor.amlEditor; split.pages.push(page); - page.$activateButton(); + //page.$activateButton(); split.editors.push(editorToUse); //console.log("setting model of ", editorToUse.id, "to", page.$model.data.xml); editorToUse.setAttribute("model", page.$model); editorToUse.setAttribute("actiontracker", page.$at); consolidateEditorSession(page, editorToUse); - addEditorListeners.call(_self, editorToUse); - - // use setTimout to circumvent the APF layout manager to go bonkers - //setTimeout(function() { - _self.show(split); - //}); + + if (!noShow) + this.show(split); } return true; }; exports.get = function(amlNode) { + if (!amlNode) + return [].concat(Splits); + var nodeName = amlNode.tagName; - var split + var split; var i = 0; var l = Splits.length; var splits = []; + var splitVar = nodeName.indexOf("page") > -1 ? "pages" : "editors"; - if (nodeName.indexOf("page") > -1) { - for (; i < l; ++i) { - split = Splits[i]; - if (!split || split.pages.indexOf(amlNode) === -1) - continue; - return [split]; - } - } - // search by editor (may yield multiple results) - else { - for (; i < l; ++i) { - split = Splits[i]; - if (!split || split.editors.indexOf(amlNode) === -1) - continue; - splits.push(split); - } + for (; i < l; ++i) { + split = Splits[i]; + if (!split || split[splitVar].indexOf(amlNode) === -1) + continue; + splits.push(split); } + return splits; }; @@ -246,6 +235,14 @@ exports.is = function(amlNode) { return !!this.get(amlNode); }; +exports.isActive = function(split) { + return split === ActiveSplit; +}; + +exports.getActive = function() { + return ActiveSplit || null; +}; + function sortEditorsAndPages(split) { // lstOpenFiles.$model.data.selectNodes("//file") var pages = tabEditors.getPages(); @@ -270,9 +267,21 @@ function sortEditorsAndPages(split) { function createEditorClones(editor) { var id = editor.tagName; + + if (!EditorClones.cloneEditor) { + EditorClones.cloneEditor = editor.cloneNode(true); + EditorClones.cloneEditor.removeAttribute("id"); + EditorClones.cloneEditor.setAttribute("visible", "false"); + apf.document.body.appendChild(EditorClones.cloneEditor); + + addEditorListeners.call(this, EditorClones.cloneEditor); + } + if (EditorClones[id] && EditorClones[id].length) return EditorClones[id]; + addEditorListeners.call(this, editor); + EditorClones[id] = []; var editor; diff --git a/client/ext/splitview/splitview.js b/client/ext/splitview/splitview.js index 1684ad2ada4..2cfc8d9e9be 100644 --- a/client/ext/splitview/splitview.js +++ b/client/ext/splitview/splitview.js @@ -13,6 +13,7 @@ var ext = require("core/ext"); var css = require("text!ext/splitview/splitview.css"); var Tabbehaviors = require("ext/tabbehaviors/tabbehaviors"); var Editors = require("ext/editors/editors"); +var Settings = require("ext/settings/settings"); var Splits = require("ext/splitview/splits"); @@ -91,6 +92,14 @@ module.exports = ext.register("ext/splitview/splitview", { Splits.update(split[0]); }); + ide.addEventListener("loadsettings", function(e) { + if (!e.model || !e.model.data) + return; + setTimeout(function() { + _self.restore(e.model.data); + }); + }); + Splits.init(this); }, @@ -117,8 +126,12 @@ module.exports = ext.register("ext/splitview/splitview", { if (idx > pages.length -1) idx = 0; + // enable split view ONLY for code editors for now... + if (pages[idx].$editor.name.indexOf("Code Editor") == -1) + return; // pass in null to mutate the active split view Splits.mutate(null, pages[idx]); + this.save(); return false; }, @@ -129,9 +142,10 @@ module.exports = ext.register("ext/splitview/splitview", { */ onFileClose: function(e) { var page = e.page; - var split = Splits.get(page); - if (split.length) - Splits.mutate(split[0], page); + var splits = Splits.get(page); + for (var i = 0, l = splits.length; i < l; ++i) + Splits.mutate(splits[i], page); + this.save(); }, /** @@ -142,13 +156,13 @@ module.exports = ext.register("ext/splitview/splitview", { */ onTabClick: function(e) { var page = e.page; + var activePage = tabEditors.getPage(); var shiftKey = e.htmlEvent.shiftKey; - var split = Splits.get(page); + var ret = null; + var split = Splits.get(activePage); split = split.length ? split[0] : null + if (split && !shiftKey) { - var activePage = tabEditors.getPage(); - var ret = null; - for (var i = 0, l = split.pages.length; i < l; ++i) { if (split.pages[i] !== activePage) continue; @@ -163,15 +177,21 @@ module.exports = ext.register("ext/splitview/splitview", { ret = false; } + if (!shiftKey) + return true; + + return ret; + } + else if (shiftKey) { + // enable split view ONLY for code editors for now... + if (page.$editor.name.indexOf("Code Editor") == -1) + return; + // tabs can be merged into and unmerged from a splitview by clicking a + // tab while holding shift + ret = !Splits.mutate(split, page); + this.save(); return ret; } - - if (!shiftKey) - return; - - // tabs can be merged into and unmerged from a splitview by clicking a - // tab while holding shift - return !Splits.mutate(split, page); }, /** @@ -232,21 +252,33 @@ module.exports = ext.register("ext/splitview/splitview", { // check if this is a valid clone session var split = Splits.get(next); split = split.length ? split[0] : null; - console.log("got split?",split); + //console.log("got split?",split); - // hide the previous clone session (if any) + // hide the previous split view if (previous && previous.$model) { var oldSplit = Splits.get(previous); oldSplit = oldSplit.length ? oldSplit[0] : null; - console.log("got old split?",oldSplit); + //console.log("got old split?",oldSplit); if (oldSplit && !split) { Splits.hide(oldSplit); + // make sure that the editor of the next page is in it's expected + // position var nextPage = next.fake ? next.relPage : next; nextPage.appendChild(next.$editor.amlEditor); } } + // enable split view ONLY for code editors for now... + if (next.$editor.name.indexOf("Code Editor") > -1) { + mnuCloneView.enable(); + mnuSplitAlign.enable(); + } + else { + mnuCloneView.disable(); + mnuSplitAlign.disable(); + } + mnuCloneView.setAttribute("checked", false); mnuSplitAlign.setAttribute("checked", true); @@ -271,25 +303,26 @@ module.exports = ext.register("ext/splitview/splitview", { }); if (split.clone) { - var editor = split.clone.$editor; + var page = split.clone; + var editor = page.$editor; mnuCloneView.setAttribute("checked", true); - if (!split.acesession) { + if (!page.acesession) { var _self = this; - split.acesession = new EditSession(doc.acedoc); - split.acesession.setUndoManager(at); + page.acesession = new EditSession(doc.acedoc); + page.acesession.setUndoManager(at); doc.addEventListener("prop.value", function(e) { - split.acesession.setValue(e.value || ""); + page.acesession.setValue(e.value || ""); editor.moveCursorTo(0, 0); }); doc.addEventListener("close", function(){ - _self.endCloneView(split.clone); + _self.endCloneView(page); }); } - editor.setProperty("value", split.acesession); + editor.amlEditor.setProperty("value", page.acesession); } else { // TODO: please test switching of tabs between normal tabs and split @@ -298,6 +331,8 @@ module.exports = ext.register("ext/splitview/splitview", { } apf.layout.forceResize(); + + this.save(); }, changeLayout: function(page, gridLayout) { @@ -312,33 +347,25 @@ module.exports = ext.register("ext/splitview/splitview", { * */ startCloneView: function(page) { - var view = this.getCloneView(page); + var split = this.getCloneView(page); var doc = page.$doc; - if (view || !doc || !this.getEditorSession(page)) + if (split || !doc || !this.getEditorSession(page)) return; - var view = createSplitView.call(this, page, 1, "clone"); - var editor = view.editors[1]; - var fake = tabEditors.add("{([@changed] == 1 ? '*' : '') + [@name]}", page.$model.data.getAttribute("path") - + "_clone", page.$editor.path, page.nextSibling || null, function(newPage){ - newPage.contentType = page.contentType; - newPage.$at = page.$at; - newPage.$doc = doc; - newPage.$editor = editor; - newPage.setAttribute("tooltip", "[@path]"); - newPage.setAttribute("class", - "{parseInt([@saving], 10) || parseInt([@lookup], 10) ? (tabEditors.getPage(tabEditors.activepage) == this ? 'saving_active' : 'saving') : \ - ([@loading] ? (tabEditors.getPage(tabEditors.activepage) == this ? 'loading_active' : 'loading') : '')}" - ); - newPage.setAttribute("model", newPage.$model = page.$model); - - view.pages.push(newPage); - view.clone = newPage; - - newPage.$activateButton(); - }); + + "_clone", page.$editor.path, page.nextSibling || null); + + fake.contentType = page.contentType; + fake.$at = page.$at; + fake.$doc = doc; + fake.$editor = page.$editor; + fake.setAttribute("tooltip", "[@path]"); + fake.setAttribute("class", + "{parseInt([@saving], 10) || parseInt([@lookup], 10) ? (tabEditors.getPage(tabEditors.activepage) == this ? 'saving_active' : 'saving') : \ + ([@loading] ? (tabEditors.getPage(tabEditors.activepage) == this ? 'loading_active' : 'loading') : '')}" + ); + fake.setAttribute("model", fake.$model = page.$model); page.addEventListener("DOMNodeRemovedFromDocument", function(e) { if (typeof tabEditors == "undefined") @@ -348,42 +375,34 @@ module.exports = ext.register("ext/splitview/splitview", { Editors.initEditorEvents(fake, page.$model); - this.updateSplitView(null, page); + Splits.mutate(null, fake); + + split = Splits.get(fake)[0]; + split.clone = fake; + + this.save(); + + return fake; }, endCloneView: function(page) { - var split, i, l, j, l2; - for (i = 0, l = this.splits.length; i < l; ++i) { - split = this.splits[i]; - if (!split || split.pages.indexOf(page) === -1 || !split.clone) - continue; - - // found it! - for (j = split.editors.length - 1; j >= 0; --j) { - if (split.editors[j] === this.$cloneEditor) - split.editors.splice(j, 1); - } - this.$cloneEditor.hide(); - apf.document.body.appendChild(this.$cloneEditor); - for (j = split.pages.length - 1; j >= 0; --j) { - if (split.pages[j] === split.clone) - split.pages.splice(j, 1); - } - tabEditors.remove(split.clone); - delete split.clone; - - updateSplitViewGrid.call(this, split); - break; - } - - mnuCloneView.setAttribute("checked", false); - apf.layout.forceResize(); + var split = this.getCloneView(page); + if (!split) + return; + + tabEditors.remove(split.clone); + delete split.clone; }, getCloneView: function(page) { - var split = this.getSplitViewByPage(page); - if (split && split.clone) - return split; + var splits = Splits.get(page); + if (!splits.length) + return null; + + for (var i = 0, l = splits.length; i < l; ++i) { + if (splits[i] && splits[i].clone) + return splits[i]; + } return null; }, @@ -392,6 +411,89 @@ module.exports = ext.register("ext/splitview/splitview", { return doc.acesession || doc.session || null; }, + save: function() { + if (!Settings.model) + return; + + var node = apf.createNodeFromXpath(Settings.model.data, "splits"); + var i, l; + for (i = node.childNodes.length - 1; i >= 0; --i) + node.removeChild(node.childNodes[i]); + + var splits = Splits.get(); + var splitEl; + for (i = 0, l = splits.length; i < l; ++i) { + splitEl = apf.getXml(""); + splitEl.setAttribute("pages", splits[i].pages.map(function(page) { + return page.id; + }).join(",")); + splitEl.setAttribute("active", Splits.isActive(splits[i]) ? "true" : "false"); + node.appendChild(splitEl); + } + apf.xmldb.applyChanges("synchronize", node); + }, + + restore: function(settings) { + // no tabs open... don't bother ;) + if (tabEditors.childNodes.length <= 1) + return; + + var nodes = settings.selectNodes("splits/split"); + if (!nodes || !nodes.length) + return; + + var tabs = tabEditors; + var activePage = tabs.getPage(); + var i, l, j, l2, ids, active, page, pages, pageSet; + for (var i = 0, l = nodes.length; i < l; ++i) { + ids = nodes[i].getAttribute("pages").split(","); + + pages = []; + pageSet = false; + for (j = 0, l2 = ids.length; j < l2; ++j) { + if (ids[j].indexOf("_clone") > -1) { + page = tabs.getPage(ids[j].replace("_clone", "")); + if (page) { + if (!pageSet) { + tabs.set(page); + pageSet = true; + } + this.startCloneView(page); + } + } + else { + page = tabs.getPage(ids[j]); + if (page) { + if (!pageSet) { + tabs.set(page); + pageSet = true; + } + Splits.mutate(null, page); + } + } + } + + if (apf.isTrue(nodes[i].getAttribute("active"))) + active = Splits.getActive(); + } + + setTimeout(function(){ + if (active) { + // waaaah dirty hack!! we show the page OTHER than the the one of + // the first page of the split view. I do this to somehow keep + // the editor sessions in sync. + var idx = tabs.childNodes.indexOf(active.pages[0]); + if (idx <= 1) + idx = tabs.childNodes.length - 1; + tabs.set(idx); + tabs.set(active.pages[0]); + Splits.update(active); + } + else + tabs.set(activePage); + }); + }, + enable : function(){ this.nodes.each(function(item){ item.enable(); From 8cde1a1ba46e6db18bbee00a2f9f0eae18ea8ae2 Mon Sep 17 00:00:00 2001 From: mikedeboer Date: Mon, 12 Dec 2011 17:51:07 +0100 Subject: [PATCH 04/69] fixed grid layout switching and restoring splits from settings --- client/ext/splitview/grids.js | 9 ++++----- client/ext/splitview/splits.js | 20 ++++++++++---------- client/ext/splitview/splitview.js | 31 ++++++++++++++----------------- 3 files changed, 28 insertions(+), 32 deletions(-) diff --git a/client/ext/splitview/grids.js b/client/ext/splitview/grids.js index 1158ce85eba..9557c3798cf 100644 --- a/client/ext/splitview/grids.js +++ b/client/ext/splitview/grids.js @@ -168,7 +168,7 @@ exports.update = function(gridLayout, split) { insertEditorAt(grid.node, split.editors[i], [].concat(grid.insertPoints[i])); // hide splitters that we don't need to see anymore - var splitters = grid.node.selectNodes("splitter"); + var splitters = grid.splitters || grid.node.selectNodes("splitter"); for (i = splitters.length - 1; i >= l - 1; --i) splitters[i].hide(); }; @@ -192,7 +192,7 @@ exports.show = function(gridLayout) { exports.hide = function(gridLayout) { gridLayout = gridLayout || defaultGrid; - var grid = GridNames[gridLayout]; + var grid = GridLayouts[gridLayout]; if (!grid || !grid.node) return; @@ -254,9 +254,8 @@ function createGridNodes(name) { createNodes(blueprint.struct, splitters); if (!blueprint.node) blueprint.node = blueprint.struct.node; - //@todo fix the splitter enumeration - //if (!blueprint.splitters) - // blueprint.splitters = splitters; + if (!blueprint.splitters) + blueprint.splitters = splitters; } function insertEditorAt(parent, editor, insertPoint) { diff --git a/client/ext/splitview/splits.js b/client/ext/splitview/splits.js index 2cca8bca15f..3ef1cf0688d 100644 --- a/client/ext/splitview/splits.js +++ b/client/ext/splitview/splits.js @@ -68,7 +68,7 @@ exports.show = function(split) { exports.hide = function(split) { split = split || ActiveSplit; - Grids.hide(split); + Grids.hide(split.gridLayout); var i, l; for (i = 0, l = split.pages.length; i < l; ++i) split.pages[i].$deactivateButton(); @@ -137,7 +137,7 @@ exports.update = function(split, gridLayout) { return this; }; -exports.mutate = function(split, page, noShow) { +exports.mutate = function(split, page) { split = split || split === null ? ActiveSplit : null; var activePage = tabEditors.getPage(); var pageIdx = split ? split.pages.indexOf(page) : -1; @@ -164,8 +164,7 @@ exports.mutate = function(split, page, noShow) { if (tabEditors.getPage() !== split.pages[0]) tabEditors.set(split.pages[0]); - if (!noShow) - this.update(split); + this.update(split); } // Add an editor to the split view else if (!split || split.editors.length < 3) { @@ -196,15 +195,13 @@ exports.mutate = function(split, page, noShow) { editorToUse = page.$editor.amlEditor; split.pages.push(page); - //page.$activateButton(); split.editors.push(editorToUse); //console.log("setting model of ", editorToUse.id, "to", page.$model.data.xml); editorToUse.setAttribute("model", page.$model); editorToUse.setAttribute("actiontracker", page.$at); consolidateEditorSession(page, editorToUse); - if (!noShow) - this.show(split); + this.show(split); } return true; @@ -284,7 +281,6 @@ function createEditorClones(editor) { EditorClones[id] = []; - var editor; for (var i = 0; i < 2; ++i) { editor = editor.cloneNode(true); editor.removeAttribute("id"); @@ -333,9 +329,13 @@ function onEditorFocus(editor) { function consolidateEditorSession(page, editor) { var session = SplitView.getEditorSession(page); - if (!session && page.$editor.setDocument) + if (!session && page.$editor.setDocument) { + var defEditor = page.$editor.amlEditor; + var oldVal = defEditor.value; page.$editor.setDocument(page.$doc, page.$at); - session = SplitView.getEditorSession(page) + session = SplitView.getEditorSession(page); + defEditor.setProperty("value", oldVal); + } if (editor.value !== session) editor.setProperty("value", session); } diff --git a/client/ext/splitview/splitview.js b/client/ext/splitview/splitview.js index 2cfc8d9e9be..7870e92e315 100644 --- a/client/ext/splitview/splitview.js +++ b/client/ext/splitview/splitview.js @@ -280,7 +280,7 @@ module.exports = ext.register("ext/splitview/splitview", { } mnuCloneView.setAttribute("checked", false); - mnuSplitAlign.setAttribute("checked", true); + mnuSplitAlign.setAttribute("checked", false); // all this must exist if (!doc || !at || !split) { @@ -341,6 +341,8 @@ module.exports = ext.register("ext/splitview/splitview", { return; Splits.update(split[0], gridLayout); + mnuSplitAlign.setAttribute("checked", gridLayout == "3rows"); + this.save(); }, /** @@ -428,6 +430,7 @@ module.exports = ext.register("ext/splitview/splitview", { return page.id; }).join(",")); splitEl.setAttribute("active", Splits.isActive(splits[i]) ? "true" : "false"); + splitEl.setAttribute("layout", splits[i].gridLayout); node.appendChild(splitEl); } apf.xmldb.applyChanges("synchronize", node); @@ -444,12 +447,13 @@ module.exports = ext.register("ext/splitview/splitview", { var tabs = tabEditors; var activePage = tabs.getPage(); - var i, l, j, l2, ids, active, page, pages, pageSet; + var i, l, j, l2, ids, active, page, pages, pageSet, gridLayout; for (var i = 0, l = nodes.length; i < l; ++i) { ids = nodes[i].getAttribute("pages").split(","); pages = []; pageSet = false; + gridLayout = nodes[i].getAttribute("layout") || null; for (j = 0, l2 = ids.length; j < l2; ++j) { if (ids[j].indexOf("_clone") > -1) { page = tabs.getPage(ids[j].replace("_clone", "")); @@ -472,26 +476,19 @@ module.exports = ext.register("ext/splitview/splitview", { } } } + if (gridLayout) + Splits.update(null, gridLayout); if (apf.isTrue(nodes[i].getAttribute("active"))) active = Splits.getActive(); } - setTimeout(function(){ - if (active) { - // waaaah dirty hack!! we show the page OTHER than the the one of - // the first page of the split view. I do this to somehow keep - // the editor sessions in sync. - var idx = tabs.childNodes.indexOf(active.pages[0]); - if (idx <= 1) - idx = tabs.childNodes.length - 1; - tabs.set(idx); - tabs.set(active.pages[0]); - Splits.update(active); - } - else - tabs.set(activePage); - }); + if (active) { + tabs.set(active.pages[0]); + Splits.update(active); + } + else + tabs.set(activePage); }, enable : function(){ From 99919babc2817dc43002b030dcc5d96cbfaeb197 Mon Sep 17 00:00:00 2001 From: mikedeboer Date: Mon, 12 Dec 2011 18:01:32 +0100 Subject: [PATCH 05/69] fixed merging pages into splits by hotkeys --- client/ext/splitview/splitview.js | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/client/ext/splitview/splitview.js b/client/ext/splitview/splitview.js index 7870e92e315..23ea747904a 100644 --- a/client/ext/splitview/splitview.js +++ b/client/ext/splitview/splitview.js @@ -116,21 +116,23 @@ module.exports = ext.register("ext/splitview/splitview", { var tabs = tabEditors; var pages = tabs.getPages(); var curr = tabs.getPage(); - var currIdx = pages.indexOf(curr); + var split = Splits.getActive(); + if (split && split.pages.indexOf(curr) > -1) + curr = split.pages[bRight ? split.pages.length - 1 : 0]; if (!curr || pages.length == 1) return; - + + var currIdx = pages.indexOf(curr); var idx = currIdx + (bRight ? 1 : -1); - if (idx < 0) - idx = pages.length - 1; - if (idx > pages.length -1) - idx = 0; + if (idx < 0 || idx > pages.length - 1) + return; // enable split view ONLY for code editors for now... if (pages[idx].$editor.name.indexOf("Code Editor") == -1) return; // pass in null to mutate the active split view Splits.mutate(null, pages[idx]); + Splits.update(); this.save(); return false; }, From 2191b51b2b2361ff7d49cef9296598d3095bb4d8 Mon Sep 17 00:00:00 2001 From: mikedeboer Date: Mon, 12 Dec 2011 18:39:46 +0100 Subject: [PATCH 06/69] fixed zen button to appear with splitmode too --- client/ext/zen/skin.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/client/ext/zen/skin.xml b/client/ext/zen/skin.xml index da8c2aaa7d2..5280b63b0d3 100644 --- a/client/ext/zen/skin.xml +++ b/client/ext/zen/skin.xml @@ -12,6 +12,7 @@ user-select : none; line-height : 17px; opacity : 0.01; + z-index: 2; } .zenbutton.full { From a2f73359045d06da84772fbaebee0126826f7ee6 Mon Sep 17 00:00:00 2001 From: mikedeboer Date: Tue, 13 Dec 2011 00:34:35 +0100 Subject: [PATCH 07/69] renamed ceEditor to amlEditor and now keeping track of it changing. Also fixed hotkey handling in editor clones --- client/ext/acebugs/acebugs.js | 12 +++--- client/ext/code/code.js | 36 ++++++++-------- client/ext/code/code.xml | 2 +- client/ext/console/console.js | 4 +- client/ext/debugger/debugger.js | 2 +- client/ext/editors/editors.js | 11 ++--- client/ext/gitblame/gitblame.js | 12 +++--- client/ext/gittools/gittools.js | 30 ++++++------- client/ext/gotoline/gotoline.js | 21 +++++----- client/ext/language/complete.js | 16 +++---- client/ext/language/language.js | 12 +++--- client/ext/language/marker.js | 3 +- client/ext/language/refactor.js | 10 +++-- client/ext/quicksearch/quicksearch.js | 22 +++++----- client/ext/quicksearch/quicksearch.xml | 1 + client/ext/quicksearch/skin.xml | 2 +- client/ext/searchinfiles/searchinfiles.js | 8 ++-- client/ext/searchreplace/searchreplace.js | 18 ++++---- client/ext/splitview/splits.js | 51 +++++++++++++++++++++-- client/ext/splitview/splitview.js | 6 +-- client/ext/stripws/stripws.js | 4 +- client/ext/vim/commands.js | 5 ++- client/ext/vim/keyboard.js | 4 +- client/ext/vim/vim.js | 12 +++--- client/ext/zen/zen.js | 12 +++--- 25 files changed, 180 insertions(+), 136 deletions(-) diff --git a/client/ext/acebugs/acebugs.js b/client/ext/acebugs/acebugs.js index 35bf7f4bde2..f3003f7c153 100644 --- a/client/ext/acebugs/acebugs.js +++ b/client/ext/acebugs/acebugs.js @@ -26,7 +26,7 @@ module.exports = ext.register("ext/acebugs/acebugs", { init: function(amlNode) { var currEditor = editors.currentEditor; if (currEditor) { - this.editorSession = currEditor.ceEditor.getSession(); + this.editorSession = currEditor.amlEditor.getSession(); this.editorSession.on("changeAnnotation", function(e) { _self.updateAnnotations(); @@ -58,7 +58,7 @@ module.exports = ext.register("ext/acebugs/acebugs", { tabEditors.addEventListener("afterswitch", function(e){ var ce = editors.currentEditor; if (ce) { - _self.editorSession = ce.ceEditor.getSession(); + _self.editorSession = ce.amlEditor.getSession(); _self.editorSession.on("changeAnnotation", function(e) { _self.updateAnnotations(); }); @@ -92,15 +92,15 @@ module.exports = ext.register("ext/acebugs/acebugs", { if (!ce || typeof mdlAceAnnotations === "undefined") return; - this.ceEditor = ce.ceEditor; - var editorSession = this.ceEditor.getSession(); + this.amlEditor = ce.amlEditor; + var editorSession = this.amlEditor.getSession(); dock.resetNotificationCount("aceAnnotations"); this.annotationWorker.postMessage(editorSession.getAnnotations()); }, goToAnnotation : function() { - line_num = dgAceAnnotations.selected.getAttribute("line"); - this.ceEditor.$editor.gotoLine(line_num); + var line_num = dgAceAnnotations.selected.getAttribute("line"); + this.amlEditor.$editor.gotoLine(line_num); }, enable: function() { diff --git a/client/ext/code/code.js b/client/ext/code/code.js index 33b4e1819c8..52f5121ff60 100644 --- a/client/ext/code/code.js +++ b/client/ext/code/code.js @@ -162,8 +162,8 @@ module.exports = ext.register("ext/code/code", { var sel = doc.getSelection(); return { - scrolltop : ceEditor.$editor.renderer.getScrollTop(), - scrollleft : ceEditor.$editor.renderer.getScrollLeft(), + scrolltop : this.amlEditor.$editor.renderer.getScrollTop(), + scrollleft : this.amlEditor.$editor.renderer.getScrollLeft(), selection : sel.getRange(), folds : folds }; @@ -178,8 +178,8 @@ module.exports = ext.register("ext/code/code", { //are those 3 lines set the values in per document base or are global for editor sel.setSelectionRange(state.selection, false); - ceEditor.$editor.renderer.scrollToY(state.scrolltop); - ceEditor.$editor.renderer.scrollToX(state.scrollleft); + this.amlEditor.$editor.renderer.scrollToY(state.scrolltop); + this.amlEditor.$editor.renderer.scrollToX(state.scrollleft); if (state.folds) { for (var i = 0, l=state.folds.length; i < l; i++) { @@ -222,15 +222,15 @@ module.exports = ext.register("ext/code/code", { }, getSelection : function(){ - if (typeof ceEditor == "undefined") + if (typeof this.amlEditor == "undefined") return null; - return ceEditor.getSelection(); + return this.amlEditor.getSelection(); }, getDocument : function(){ - if (typeof ceEditor == "undefined") + if (typeof this.amlEditor == "undefined") return null; - return ceEditor.getSession(); + return this.amlEditor.getSession(); }, setDocument : function(doc, actiontracker){ @@ -260,7 +260,7 @@ module.exports = ext.register("ext/code/code", { return doc.acesession.getValue(); }); } - ceEditor.setProperty("value", doc.acesession); + this.amlEditor.setProperty("value", doc.acesession); }, hook: function() { @@ -288,11 +288,11 @@ module.exports = ext.register("ext/code/code", { }, init: function(amlPage) { - amlPage.appendChild(ceEditor); - ceEditor.show(); - this.ceEditor = this.amlEditor = ceEditor; - ceEditor.$editor.commands = this.commandManager; + amlPage.appendChild(this.amlEditor); + this.amlEditor.show(); + + this.amlEditor.$editor.commands = this.commandManager; var _self = this; @@ -361,16 +361,16 @@ module.exports = ext.register("ext/code/code", { }; ide.addEventListener("keybindingschange", function(e) { - if (typeof ceEditor == "undefined") + if (typeof _self.amlEditor == "undefined") return; var bindings = e.keybindings.code; - ceEditor.$editor.setKeyboardHandler(new HashHandler(bindings)); + _self.amlEditor.$editor.setKeyboardHandler(new HashHandler(bindings)); // In case the `keybindingschange` event gets fired after other // plugins that change keybindings have already changed them (i.e. // the vim plugin), we fire an event so these plugins can react to it. ide.dispatchEvent("code.ext:defaultbindingsrestored", { - bindings: ceEditor.$editor.getKeyboardHandler() + bindings: _self.amlEditor.$editor.getKeyboardHandler() }); }); }, @@ -442,8 +442,8 @@ module.exports = ext.register("ext/code/code", { item.destroy(true, true); }); - if (self.ceEditor) { - ceEditor.destroy(true, true); + if (this.amlEditor) { + this.amlEditor.destroy(true, true); mnuSyntax.destroy(true, true); } diff --git a/client/ext/code/code.xml b/client/ext/code/code.xml index a63bb2824c2..b538fca0a1c 100644 --- a/client/ext/code/code.xml +++ b/client/ext/code/code.xml @@ -41,7 +41,7 @@ require('ext/quickwatch/quickwatch').toggleDialog(1, true); ">Quick Watch - Select All + Select All diff --git a/client/ext/console/console.js b/client/ext/console/console.js index 4b03f9b12f3..c4080468463 100644 --- a/client/ext/console/console.js +++ b/client/ext/console/console.js @@ -107,8 +107,8 @@ module.exports = ext.register("ext/console/console", { switchconsole : function() { if (apf.activeElement == txtConsoleInput) { - if (window.ceEditor) { - ceEditor.focus(); + if (editors.currentEditor.amlEditor) { + editors.currentEditor.amlEditor.focus(); this.disable(); } } diff --git a/client/ext/debugger/debugger.js b/client/ext/debugger/debugger.js index 7e4099ecda2..692323c25a7 100644 --- a/client/ext/debugger/debugger.js +++ b/client/ext/debugger/debugger.js @@ -247,7 +247,7 @@ module.exports = ext.register("ext/debugger/debugger", { var file = fs.model.queryNode("//file[@scriptid='" + scriptId + "']"); // check prerequisites - if (!ceEditor.$updateMarkerPrerequisite()) { + if (!editors.currentEditor.amlEditor.$updateMarkerPrerequisite()) { return; } diff --git a/client/ext/editors/editors.js b/client/ext/editors/editors.js index f0ff380bad3..cefbe4c71b2 100644 --- a/client/ext/editors/editors.js +++ b/client/ext/editors/editors.js @@ -344,8 +344,8 @@ module.exports = ext.register("ext/editors/editors", { // okay don't know if you would want this, but this is the way the 'open file' dialog // handles it so let's do that setTimeout(function () { - if (typeof ceEditor !== "undefined") - ceEditor.focus(); + if (typeof editor.amlEditor !== "undefined") + editor.amlEditor.focus(); }, 100); settings.save(); @@ -734,13 +734,14 @@ module.exports = ext.register("ext/editors/editors", { var hasData = page && (tabEditors.getPage(path) || { }).$doc ? true : false; if (row !== undefined) { + var editor = _self.currentEditor.amlEditor; var jumpTo = function(){ setTimeout(function() { // TODO move this to the editor - ceEditor.$editor.gotoLine(row, column); + editor.$editor.gotoLine(row, column); if (text) - ceEditor.$editor.find(text); - ceEditor.focus(); + editor.$editor.find(text); + editor.focus(); }, 100); }; diff --git a/client/ext/gitblame/gitblame.js b/client/ext/gitblame/gitblame.js index 43c21b901fa..3fea6064a43 100644 --- a/client/ext/gitblame/gitblame.js +++ b/client/ext/gitblame/gitblame.js @@ -22,7 +22,7 @@ module.exports = ext.register("ext/gitblame/gitblame", { init : function(amlNode){ this.blamejs = new BlameJS(); - this.originalGutterWidth = editors.currentEditor.ceEditor.$editor.renderer.getGutterWidth(); + this.originalGutterWidth = editors.currentEditor.amlEditor.$editor.renderer.getGutterWidth(); }, hook : function(){ @@ -32,8 +32,8 @@ module.exports = ext.register("ext/gitblame/gitblame", { tabEditors.addEventListener("beforeswitch", function(e){ if (editors.currentEditor) { - editors.currentEditor.ceEditor.$editor.renderer.$gutterLayer.setExtendedAnnotationTextArr([]); - editors.currentEditor.ceEditor.$editor.renderer.setGutterWidth(_self.originalGutterWidth + "px"); + editors.currentEditor.amlEditor.$editor.renderer.$gutterLayer.setExtendedAnnotationTextArr([]); + editors.currentEditor.amlEditor.$editor.renderer.setGutterWidth(_self.originalGutterWidth + "px"); } }); @@ -73,7 +73,7 @@ module.exports = ext.register("ext/gitblame/gitblame", { else { ide.send(JSON.stringify(data)); // Set gutter width - editors.currentEditor.ceEditor.$editor.renderer.setGutterWidth("300px"); + editors.currentEditor.amlEditor.$editor.renderer.setGutterWidth("300px"); } } } @@ -128,8 +128,8 @@ module.exports = ext.register("ext/gitblame/gitblame", { }; } } - editors.currentEditor.ceEditor.$editor.renderer.$gutterLayer.setExtendedAnnotationTextArr(textHash); - editors.currentEditor.ceEditor.$editor.renderer.updateFull(); + editors.currentEditor.amlEditor.$editor.renderer.$gutterLayer.setExtendedAnnotationTextArr(textHash); + editors.currentEditor.amlEditor.$editor.renderer.updateFull(); }, enable : function(){ diff --git a/client/ext/gittools/gittools.js b/client/ext/gittools/gittools.js index ba65476cf55..86c561dd286 100644 --- a/client/ext/gittools/gittools.js +++ b/client/ext/gittools/gittools.js @@ -60,9 +60,9 @@ module.exports = ext.register("ext/gittools/gittools", { if (!_self.gitLogs[file]) _self.gitLog(); /*if (editors.currentEditor) { - editors.currentEditor.ceEditor.$editor.renderer.$gutterLayer.setExtendedAnnotationTextArr([]); + editors.currentEditor.amlEditor.$editor.renderer.$gutterLayer.setExtendedAnnotationTextArr([]); if (_self.originalGutterWidth) - editors.currentEditor.ceEditor.$editor.renderer.setGutterWidth(_self.originalGutterWidth + "px"); + editors.currentEditor.amlEditor.$editor.renderer.setGutterWidth(_self.originalGutterWidth + "px"); }*/ }); }, @@ -121,7 +121,7 @@ module.exports = ext.register("ext/gittools/gittools", { logData : [], lastLoadedGitLog : 0, lastSliderValue : 0, - currentRevision : editors.currentEditor ? editors.currentEditor.ceEditor.getSession().getValue() : "", + currentRevision : editors.currentEditor ? editors.currentEditor.amlEditor.getSession().getValue() : "", revisions : {} }; } @@ -159,10 +159,10 @@ module.exports = ext.register("ext/gittools/gittools", { else { ide.send(JSON.stringify(data)); if (!this.originalGutterWidth) - this.originalGutterWidth = editors.currentEditor.ceEditor.$editor.renderer.getGutterWidth(); + this.originalGutterWidth = editors.currentEditor.amlEditor.$editor.renderer.getGutterWidth(); // Set gutter width, arbitrary number based on 12/13px font - editors.currentEditor.ceEditor.$editor.renderer.setGutterWidth("300px"); + editors.currentEditor.amlEditor.$editor.renderer.setGutterWidth("300px"); } } } @@ -229,8 +229,8 @@ module.exports = ext.register("ext/gittools/gittools", { onGitShowMessage : function(message) { this.gitLogs[message.body.file].revisions[message.body.hash] = message.body.out; - editors.currentEditor.ceEditor.getSession().setValue(message.body.out); - editors.currentEditor.ceEditor.$editor.setReadOnly(true); + editors.currentEditor.amlEditor.getSession().setValue(message.body.out); + editors.currentEditor.amlEditor.$editor.setReadOnly(true); }, onGitBlameMessage: function(message) { @@ -263,9 +263,9 @@ module.exports = ext.register("ext/gittools/gittools", { sliderGitLog.setValue(this.gitLogs[file].lastSliderValue); this.formulateGitLogOut(this.gitLogs[file].lastSliderValue); if (this.gitLogs[file].lastLoadedGitLog != this.gitLogs[file].logData.length) { - editors.currentEditor.ceEditor.$editor.setReadOnly(true); + editors.currentEditor.amlEditor.$editor.setReadOnly(true); } else { - editors.currentEditor.ceEditor.$editor.setReadOnly(false); + editors.currentEditor.amlEditor.$editor.setReadOnly(false); } } else { lblGitLog.setAttribute("caption", fileName + " revisions (0)"); @@ -278,7 +278,7 @@ module.exports = ext.register("ext/gittools/gittools", { btnGitBlame.enable(); txtGitLog.setValue(""); if (editors.currentEditor) - editors.currentEditor.ceEditor.$editor.setReadOnly(false); + editors.currentEditor.amlEditor.$editor.setReadOnly(false); } }, @@ -327,14 +327,14 @@ module.exports = ext.register("ext/gittools/gittools", { loadFileRevision : function() { var file = this.getFilePath(); if (sliderGitLog.value == this.gitLogs[file].logData.length) { - editors.currentEditor.ceEditor.getSession().setValue( + editors.currentEditor.amlEditor.getSession().setValue( this.gitLogs[file].currentRevision ); - editors.currentEditor.ceEditor.$editor.setReadOnly(false); + editors.currentEditor.amlEditor.$editor.setReadOnly(false); } else { // Save the latest version of the file if (this.gitLogs[file].lastLoadedGitLog == this.gitLogs[file].logData.length) - this.gitLogs[file].currentRevision = editors.currentEditor.ceEditor.getSession().getValue(); + this.gitLogs[file].currentRevision = editors.currentEditor.amlEditor.getSession().getValue(); this.gitShow(this.gitLogs[file].logData[sliderGitLog.value].commit); } @@ -361,8 +361,8 @@ module.exports = ext.register("ext/gittools/gittools", { } } - editors.currentEditor.ceEditor.$editor.renderer.$gutterLayer.setExtendedAnnotationTextArr(textHash); - editors.currentEditor.ceEditor.$editor.renderer.updateFull(); + editors.currentEditor.amlEditor.$editor.renderer.$gutterLayer.setExtendedAnnotationTextArr(textHash); + editors.currentEditor.amlEditor.$editor.renderer.updateFull(); }, enable : function(){ diff --git a/client/ext/gotoline/gotoline.js b/client/ext/gotoline/gotoline.js index 27c5eea3b61..2f306d1a54e 100644 --- a/client/ext/gotoline/gotoline.js +++ b/client/ext/gotoline/gotoline.js @@ -120,12 +120,12 @@ module.exports = ext.register("ext/gotoline/gotoline", { if (!editorPage) return; var editor = editors.currentEditor; - if (!editor || !editor.ceEditor) + if (!editor || !editor.amlEditor) return; if (!force && !winGotoLine.visible || force > 0) { - var ace = editor.ceEditor.$editor; - var aceHtml = editor.ceEditor.$ext; + var ace = editor.amlEditor.$editor; + var aceHtml = editor.amlEditor.$ext; var cursor = ace.getCursorPosition(); //Set the current line @@ -136,7 +136,8 @@ module.exports = ext.register("ext/gotoline/gotoline", { var epos = apf.getAbsolutePosition(aceHtml); var maxTop = aceHtml.offsetHeight - 100; - editor.ceEditor.parentNode.appendChild(winGotoLine); + //editor.amlEditor.parentNode.appendChild(winGotoLine); + editor.amlEditor.parentNode.insertBefore(winGotoLine, editor.amlEditor); winGotoLine.setAttribute("top", Math.min(maxTop, pos.pageY - epos[1])); winGotoLine.setAttribute("left", -60); @@ -166,7 +167,7 @@ module.exports = ext.register("ext/gotoline/gotoline", { control : (this.control = {}), onfinish : function(){ winGotoLine.hide(); - editor.ceEditor.focus(); + editor.amlEditor.focus(); } }); } @@ -175,12 +176,12 @@ module.exports = ext.register("ext/gotoline/gotoline", { }, execGotoLine: function(line) { - var editor = require('ext/editors/editors').currentEditor; - if (!editor || !editor.ceEditor) + var editor = editors.currentEditor; + if (!editor || !editor.amlEditor) return; - var ceEditor = editor.ceEditor; - var ace = ceEditor.$editor; + var amlEditor = editor.amlEditor; + var ace = amlEditor.$editor; winGotoLine.hide(); @@ -200,7 +201,7 @@ module.exports = ext.register("ext/gotoline/gotoline", { apf.xmldb.appendChild(gotoline, lineEl, gotoline.firstChild); ace.gotoLine(line); - ceEditor.focus(); + amlEditor.focus(); }, enable : function(){ diff --git a/client/ext/language/complete.js b/client/ext/language/complete.js index 054d5bc5997..5644ad23df8 100644 --- a/client/ext/language/complete.js +++ b/client/ext/language/complete.js @@ -69,7 +69,7 @@ module.exports = { showCompletionBox: function(matches, prefix) { var _self = this; this.editor = editors.currentEditor; - var ace = this.editor.ceEditor.$editor; + var ace = this.editor.amlEditor.$editor; this.selectedIdx = 0; this.scrollIdx = 0; this.matchEls = []; @@ -77,7 +77,7 @@ module.exports = { this.matches = matches; this.completionElement = txtCompleter.$ext; this.cursorConfig = ace.renderer.$cursorLayer.config; - var style = dom.computedStyle(this.editor.ceEditor.$ext); + var style = dom.computedStyle(this.editor.amlEditor.$ext); this.completionElement.style.fontSize = style.fontSize; //this.completionElement.style.maxHeight = 10 * this.cursorConfig.lineHeight; @@ -116,7 +116,7 @@ module.exports = { }, closeCompletionBox : function(event, doNotHide) { - var ace = editors.currentEditor.ceEditor.$editor; + var ace = editors.currentEditor.amlEditor.$editor; if (!doNotHide) barCompleterCont.$ext.style.display = "none"; document.removeEventListener("click", this.closeCompletionBox); @@ -137,7 +137,7 @@ module.exports = { matchEl.innerHTML += '' + match.meta + ''; } matchEl.addEventListener("click", function() { - var editor = editors.currentEditor.ceEditor.$editor; + var editor = editors.currentEditor.amlEditor.$editor; replaceText(editor, _self.prefix, match.replaceText); editor.focus(); }); @@ -154,7 +154,7 @@ module.exports = { else if(keyCode === 9 && e.shiftKey) // Shift-Tab keyCode = 38; // Down - var keyBinding = editors.currentEditor.ceEditor.$editor.keyBinding; + var keyBinding = editors.currentEditor.amlEditor.$editor.keyBinding; switch(keyCode) { case 0: break; @@ -180,7 +180,7 @@ module.exports = { e.preventDefault(); break; case 13: // Enter - var editor = editors.currentEditor.ceEditor.$editor; + var editor = editors.currentEditor.amlEditor.$editor; replaceText(editor, this.prefix, this.matches[this.selectedIdx].replaceText); this.closeCompletionBox(); e.preventDefault(); @@ -224,7 +224,7 @@ module.exports = { }, invoke: function(forceBox) { - var editor = editors.currentEditor.ceEditor.$editor; + var editor = editors.currentEditor.amlEditor.$editor; this.forceBox = forceBox; // This is required to ensure the updated document text has been sent to the worker before the 'complete' message var worker = this.worker; @@ -234,7 +234,7 @@ module.exports = { }, onComplete: function(event) { - var editor = editors.currentEditor.ceEditor.$editor; + var editor = editors.currentEditor.amlEditor.$editor; var pos = editor.getCursorPosition(); var line = editor.getSession().getLine(pos.row); var identifier = retrievePreceedingIdentifier(line, pos.column); diff --git a/client/ext/language/language.js b/client/ext/language/language.js index 366cbae22cc..dd68e0e0ed4 100644 --- a/client/ext/language/language.js +++ b/client/ext/language/language.js @@ -56,11 +56,11 @@ module.exports = ext.register("ext/language/language", { ide.addEventListener("afteropenfile", function(event){ if (!event.node) return; - if (!editors.currentEditor || !editors.currentEditor.ceEditor) // No editor, for some reason + if (!editors.currentEditor || !editors.currentEditor.amlEditor) // No editor, for some reason return; ext.initExtension(_self); var path = event.node.getAttribute("path"); - worker.call("switchFile", [path, editors.currentEditor.ceEditor.syntax, event.doc.getValue()]); + worker.call("switchFile", [path, editors.currentEditor.amlEditor.syntax, event.doc.getValue()]); event.doc.addEventListener("close", function() { worker.emit("documentClose", {data: path}); }); @@ -84,9 +84,9 @@ module.exports = ext.register("ext/language/language", { var _self = this; var worker = this.worker; apf.importCssString(css); - if (!editors.currentEditor || !editors.currentEditor.ceEditor) + if (!editors.currentEditor || !editors.currentEditor.amlEditor) return; - this.editor = editors.currentEditor.ceEditor.$editor; + this.editor = editors.currentEditor.amlEditor.$editor; this.$onCursorChange = this.onCursorChangeDefer.bind(this); this.editor.selection.on("changeCursor", this.$onCursorChange); var oldSelection = this.editor.selection; @@ -119,9 +119,9 @@ module.exports = ext.register("ext/language/language", { setPath: function() { var currentPath = tabEditors.getPage().getAttribute("id"); // Currently no code editor active - if(!editors.currentEditor.ceEditor) + if(!editors.currentEditor.amlEditor) return; - this.worker.call("switchFile", [currentPath, editors.currentEditor.ceEditor.syntax, this.editor.getSession().getValue(), this.editor.getCursorPosition()]); + this.worker.call("switchFile", [currentPath, editors.currentEditor.amlEditor.syntax, this.editor.getSession().getValue(), this.editor.getCursorPosition()]); }, setJSHint: function() { diff --git a/client/ext/language/marker.js b/client/ext/language/marker.js index 405a38150c5..0f4160dc217 100644 --- a/client/ext/language/marker.js +++ b/client/ext/language/marker.js @@ -8,6 +8,7 @@ define(function(require, exports, module) { var Range = require("ace/range").Range; var Anchor = require('ace/anchor').Anchor; +var Editors = require("ext/editors/editors"); module.exports = { disabledMarkerTypes: {}, @@ -84,7 +85,7 @@ module.exports = { */ disableMarkerType: function(type) { this.disabledMarkerTypes[type] = true; - var session = ceEditor.$editor.session; + var session = Editors.currentEditor.amlEditor.$editor.session; var markers = session.getMarkers(false); for (var id in markers) { // All language analysis' markers are prefixed with language_highlight diff --git a/client/ext/language/refactor.js b/client/ext/language/refactor.js index 3d024736f98..380e7c53a10 100644 --- a/client/ext/language/refactor.js +++ b/client/ext/language/refactor.js @@ -6,6 +6,7 @@ */ define(function(require, exports, module) { +var Editors = require("ext/editors/editors"); var PlaceHolder = require("ace/placeholder").PlaceHolder; var marker = require("ext/language/marker"); var ide = require("core/ide"); @@ -73,12 +74,13 @@ module.exports = { // Temporarily disable these markers, to prevent weird slow-updating events whilst typing marker.disableMarkerType('occurrence_main'); marker.disableMarkerType('occurrence_other'); - var cursor = ceEditor.$editor.getCursorPosition(); + var ace = Editors.currentEditor.amlEditor; + var cursor = ace.getCursorPosition(); var mainPos = data.pos; - var p = new PlaceHolder(ceEditor.$editor.session, data.length, mainPos, data.others, "language_rename_main", "language_rename_other"); + var p = new PlaceHolder(ace.session, data.length, mainPos, data.others, "language_rename_main", "language_rename_other"); if(cursor.row !== mainPos.row || cursor.column < mainPos.column || cursor.column > mainPos.column + data.length) { // Cursor is not "inside" the main identifier, move it there - ceEditor.$editor.moveCursorTo(mainPos.row, mainPos.column); + ace.moveCursorTo(mainPos.row, mainPos.column); } p.showOtherMarkers(); p.on("cursorLeave", function() { @@ -89,7 +91,7 @@ module.exports = { }, renameVariable: function() { - this.worker.emit("fetchVariablePositions", {data: ceEditor.$editor.getCursorPosition()}); + this.worker.emit("fetchVariablePositions", {data: Editors.currentEditor.amlEditor.$editor.getCursorPosition()}); } }; diff --git a/client/ext/quicksearch/quicksearch.js b/client/ext/quicksearch/quicksearch.js index 2fe73609352..dfe2364bc96 100644 --- a/client/ext/quicksearch/quicksearch.js +++ b/client/ext/quicksearch/quicksearch.js @@ -91,10 +91,6 @@ module.exports = ext.register("ext/quicksearch/quicksearch", { if (!apf.isChildOf(winQuickSearch, e.toElement)) _self.toggleDialog(-1); }); - - var editor = editors.currentEditor; - if (editor && editor.ceEditor) - editor.ceEditor.parentNode.appendChild(winQuickSearch); }, navigateList : function(type){ @@ -190,8 +186,10 @@ module.exports = ext.register("ext/quicksearch/quicksearch", { if (!editorPage) return; var editor = editors.currentEditor; - if (!editor || !editor.ceEditor) + if (!editor || !editor.amlEditor) return; + + editor.amlEditor.parentNode.appendChild(winQuickSearch); var _self = this; @@ -203,8 +201,8 @@ module.exports = ext.register("ext/quicksearch/quicksearch", { var range = sel.getRange(); var value = doc.getTextRange(range); - if (!value && editor.ceEditor) - value = editor.ceEditor.getLastSearchOptions().needle; + if (!value && editor.amlEditor) + value = editor.amlEditor.getLastSearchOptions().needle; if (value) txtQuickSearch.setValue(value); @@ -243,7 +241,7 @@ module.exports = ext.register("ext/quicksearch/quicksearch", { control : (this.control = {}), onfinish : function(){ winQuickSearch.hide(); - editor.ceEditor.focus(); + editor.amlEditor.focus(); } }); } @@ -292,7 +290,7 @@ module.exports = ext.register("ext/quicksearch/quicksearch", { if (close) { winQuickSearch.hide(); - editors.currentEditor.ceEditor.focus(); + editors.currentEditor.amlEditor.focus(); } this.updateCounter(); @@ -327,11 +325,11 @@ module.exports = ext.register("ext/quicksearch/quicksearch", { $getAce: function() { var editor = editors.currentEditor; - if (!editor || !editor.ceEditor) + if (!editor || !editor.amlEditor) return; - var ceEditor = editor.ceEditor; - return ceEditor.$editor; + var amlEditor = editor.amlEditor; + return amlEditor.$editor; }, enable : function(){ diff --git a/client/ext/quicksearch/quicksearch.xml b/client/ext/quicksearch/quicksearch.xml index 00163867d5d..541042b73fe 100644 --- a/client/ext/quicksearch/quicksearch.xml +++ b/client/ext/quicksearch/quicksearch.xml @@ -10,6 +10,7 @@ kbclose = "true" top = "-27" right = "30" + zindex = "10000" focussable = "true"> diff --git a/client/ext/quicksearch/skin.xml b/client/ext/quicksearch/skin.xml index bced332d0b7..b483725ba0d 100644 --- a/client/ext/quicksearch/skin.xml +++ b/client/ext/quicksearch/skin.xml @@ -3,7 +3,7 @@ -1; + + if (!EditorClones.cloneEditor && isCodeEditor) { + if (!previousEditor) + previousEditor = editor; EditorClones.cloneEditor = editor.cloneNode(true); EditorClones.cloneEditor.removeAttribute("id"); EditorClones.cloneEditor.setAttribute("visible", "false"); @@ -288,6 +320,11 @@ function createEditorClones(editor) { EditorClones[id].push(editor); apf.document.body.appendChild(editor); addEditorListeners.call(this, editor); + if (isCodeEditor) { + editor.$editor.commands = previousEditor.$editor.commands; + if (previousEditor.$editor.getKeyboardHandler()) + editor.$editor.setKeyboardHandler(previousEditor.$editor.getKeyboardHandler()); + } } return EditorClones[id]; @@ -313,8 +350,16 @@ function removeEditorListeners(editor) { delete editor.$splitListener; } +var previousEditor; + function onEditorFocus(editor) { var splits = exports.get(editor); + + if (Editors.currentEditor.name.indexOf("Code Editor") > -1) { + if (!previousEditor) + previousEditor = Editors.currentEditor.amlEditor; + Editors.currentEditor.amlEditor = editor; + } splits.forEach(function(split) { var activePage = split.pages[split.editors.indexOf(editor)]; diff --git a/client/ext/splitview/splitview.js b/client/ext/splitview/splitview.js index 23ea747904a..faac8c1cab5 100644 --- a/client/ext/splitview/splitview.js +++ b/client/ext/splitview/splitview.js @@ -162,7 +162,7 @@ module.exports = ext.register("ext/splitview/splitview", { var shiftKey = e.htmlEvent.shiftKey; var ret = null; var split = Splits.get(activePage); - split = split.length ? split[0] : null + split = split.length ? split[0] : null; if (split && !shiftKey) { for (var i = 0, l = split.pages.length; i < l; ++i) { @@ -310,8 +310,6 @@ module.exports = ext.register("ext/splitview/splitview", { mnuCloneView.setAttribute("checked", true); if (!page.acesession) { - var _self = this; - page.acesession = new EditSession(doc.acedoc); page.acesession.setUndoManager(at); @@ -450,7 +448,7 @@ module.exports = ext.register("ext/splitview/splitview", { var tabs = tabEditors; var activePage = tabs.getPage(); var i, l, j, l2, ids, active, page, pages, pageSet, gridLayout; - for (var i = 0, l = nodes.length; i < l; ++i) { + for (i = 0, l = nodes.length; i < l; ++i) { ids = nodes[i].getAttribute("pages").split(","); pages = []; diff --git a/client/ext/stripws/stripws.js b/client/ext/stripws/stripws.js index 699da785f97..caaaaa374a5 100644 --- a/client/ext/stripws/stripws.js +++ b/client/ext/stripws/stripws.js @@ -20,10 +20,10 @@ var RE_WS = /[ \t\r\f\v]+\n/g; // Attaching to exports.module for testing purposes var strip = module.exports.strip = function () { - if (!editors.currentEditor.ceEditor) + if (!editors.currentEditor.amlEditor) return; - var editor = editors.currentEditor.ceEditor.$editor; + var editor = editors.currentEditor.amlEditor.$editor; var session = editor.getSession(); var source = session.getValue(); var selection = session.getSelection(); diff --git a/client/ext/vim/commands.js b/client/ext/vim/commands.js index d658c2850ac..a952047f5cd 100644 --- a/client/ext/vim/commands.js +++ b/client/ext/vim/commands.js @@ -10,6 +10,7 @@ define(function(require, exports, module) { "use strict"; +var Editors = require("ext/editors/editors"); var util = require("ext/vim/maps/util"); var motions = require("ext/vim/maps/motions"); var operators = require("ext/vim/maps/operators"); @@ -454,11 +455,11 @@ exports.commands = { }; var handleCursorMove = exports.onCursorMove = function() { - var editor = ceEditor.$editor; + var editor = Editors.currentEditor.amlEditor.$editor; if(util.currentMode === 'insert' || handleCursorMove.running) return; - else if(!ceEditor.$editor.selection.isEmpty()) { + else if(!editor.selection.isEmpty()) { handleCursorMove.running = true; if(util.onVisualLineMode) { var originRow = editor.selection.visualLineStart; diff --git a/client/ext/vim/keyboard.js b/client/ext/vim/keyboard.js index ab2b7587d9d..4786b5ebe8e 100644 --- a/client/ext/vim/keyboard.js +++ b/client/ext/vim/keyboard.js @@ -17,10 +17,10 @@ var matchChar = function(buffer, hashId, key, symbolicName, keyId) { if (matched) { if (keyId) { - keyId = String.fromCharCode(parseInt(keyId.replace("U+", "0x"))); + keyId = String.fromCharCode(parseInt(keyId.replace("U+", "0x"), 10)); } - var editor = editors.currentEditor.ceEditor.$editor; + var editor = editors.currentEditor.amlEditor.$editor; editor.commands.addCommand({ name: "builder", exec: function(editor) { diff --git a/client/ext/vim/vim.js b/client/ext/vim/vim.js index 119a6ec2b4a..6fe5e0ca5fb 100644 --- a/client/ext/vim/vim.js +++ b/client/ext/vim/vim.js @@ -26,7 +26,7 @@ var OLD_HANDLER; var onConsoleCommand = function onConsoleCommand(e) { var cmd = e.data.command; - var domEditor = editors.currentEditor.ceEditor; + var domEditor = editors.currentEditor.amlEditor; if (cmd && typeof cmd === "string") { if (cmd[0] === ":") { cmd = cmd.substr(1); @@ -81,10 +81,10 @@ var onCursorMove = function() { }; var enableVim = function enableVim() { - if (editors.currentEditor && editors.currentEditor.ceEditor) { + if (editors.currentEditor && editors.currentEditor.amlEditor) { ext.initExtension(this); - var editor = editors.currentEditor.ceEditor.$editor; + var editor = editors.currentEditor.amlEditor.$editor; addCommands(editor, commands); editor.renderer.container.addEventListener("click", onCursorMove, false); @@ -102,8 +102,8 @@ var enableVim = function enableVim() { }; var disableVim = function() { - if (editors.currentEditor && editors.currentEditor.ceEditor) { - var editor = editors.currentEditor.ceEditor.$editor; + if (editors.currentEditor && editors.currentEditor.amlEditor) { + var editor = editors.currentEditor.amlEditor.$editor; removeCommands(editor, commands); editor.setKeyboardHandler(OLD_HANDLER); @@ -116,7 +116,7 @@ var disableVim = function() { var cliKeyDown = function(e) { if (e.keyCode === 27) { // ESC is pressed in the CLI txtConsoleInput.blur(); - editors.currentEditor.ceEditor.focus(); + editors.currentEditor.amlEditor.focus(); } }; diff --git a/client/ext/zen/zen.js b/client/ext/zen/zen.js index cff3897bcea..18a9f7e33a4 100644 --- a/client/ext/zen/zen.js +++ b/client/ext/zen/zen.js @@ -92,8 +92,8 @@ module.exports = ext.register("ext/zen/zen", { this.setupHandleListeners(); var editor = editors.currentEditor; - if (editor && editor.ceEditor) - editor.ceEditor.parentNode.appendChild(btnZenFullscreen); + if (editor && editor.amlEditor) + editor.amlEditor.parentNode.appendChild(btnZenFullscreen); vbMain.parentNode.appendChild(new apf.vbox({ anchors: "0 0 0 0", @@ -288,7 +288,7 @@ module.exports = ext.register("ext/zen/zen", { }, 0.5); setTimeout(function() { - ceEditor.focus(); + editors.currentEditor.amlEditor.focus(); apf.layout.forceResize(tabEditors.parentNode.$ext); }, 100); }); @@ -341,7 +341,7 @@ module.exports = ext.register("ext/zen/zen", { apf.layout.forceResize(); setTimeout(function() { - ceEditor.focus(); + editors.currentEditor.amlEditor.focus(); }, 100); } }, @@ -392,7 +392,7 @@ module.exports = ext.register("ext/zen/zen", { apf.layout.forceResize(tabEditors.parentNode.$ext); setTimeout(function() { - ceEditor.focus(); + editors.currentEditor.amlEditor.focus(); apf.layout.forceResize(tabEditors.parentNode.$ext); }, 100); }); @@ -412,7 +412,7 @@ module.exports = ext.register("ext/zen/zen", { vbZen.hide(); apf.layout.forceResize(); setTimeout(function() { - ceEditor.focus(); + editors.currentEditor.amlEditor.focus(); }, 100); } From 4e4b0cedc849b663bf7e59a245d3cf273ffb736c Mon Sep 17 00:00:00 2001 From: mikedeboer Date: Wed, 14 Dec 2011 16:59:43 +0100 Subject: [PATCH 08/69] correcting gotoline and quicksearch dialog positioning for splitview and Zen mode button reparenting between editor switches --- client/ext/gotoline/gotoline.js | 11 ++- client/ext/quicksearch/quicksearch.js | 9 ++- client/ext/splitview/grids.js | 12 ++++ client/ext/splitview/splits.js | 98 +++++++++++++++++++++++++++ client/ext/splitview/splitview.js | 2 + client/ext/zen/skin.xml | 4 +- client/ext/zen/zen.js | 17 ++++- 7 files changed, 147 insertions(+), 6 deletions(-) diff --git a/client/ext/gotoline/gotoline.js b/client/ext/gotoline/gotoline.js index 2f306d1a54e..db103916675 100644 --- a/client/ext/gotoline/gotoline.js +++ b/client/ext/gotoline/gotoline.js @@ -7,6 +7,7 @@ define(function(require, exports, module) { +var ide = require("core/ide"); var ext = require("core/ext"); var code = require("ext/code/code"); var editors = require("ext/editors/editors"); @@ -135,6 +136,9 @@ module.exports = ext.register("ext/gotoline/gotoline", { var pos = ace.renderer.textToScreenCoordinates(cursor.row, cursor.column); var epos = apf.getAbsolutePosition(aceHtml); var maxTop = aceHtml.offsetHeight - 100; + var corrected = ide.dispatchEvent("ext.gotoline.correctpos", { + pos: pos + }); //editor.amlEditor.parentNode.appendChild(winGotoLine); editor.amlEditor.parentNode.insertBefore(winGotoLine, editor.amlEditor); @@ -143,13 +147,18 @@ module.exports = ext.register("ext/gotoline/gotoline", { winGotoLine.show(); txtLineNr.focus(); + + if (corrected) { + if (typeof corrected.top != "undefined") + winGotoLine.$ext.style.top = corrected.top + "px"; + } //Animate apf.tween.single(winGotoLine, { type : "left", anim : apf.tween.easeInOutCubic, from : -60, - to : 0, + to : (corrected && typeof corrected.left != "undefined") ? corrected.left : 0, steps : 8, interval : 10, control : (this.control = {}) diff --git a/client/ext/quicksearch/quicksearch.js b/client/ext/quicksearch/quicksearch.js index dfe2364bc96..51f424f9ef9 100644 --- a/client/ext/quicksearch/quicksearch.js +++ b/client/ext/quicksearch/quicksearch.js @@ -7,6 +7,7 @@ define(function(require, exports, module) { +var ide = require("core/ide"); var ext = require("core/ext"); var code = require("ext/code/code"); var editors = require("ext/editors/editors"); @@ -200,6 +201,7 @@ module.exports = ext.register("ext/quicksearch/quicksearch", { var doc = editor.getDocument(); var range = sel.getRange(); var value = doc.getTextRange(range); + var corrected = ide.dispatchEvent("ext.quicksearch.correctpos"); if (!value && editor.amlEditor) value = editor.amlEditor.getLastSearchOptions().needle; @@ -211,13 +213,18 @@ module.exports = ext.register("ext/quicksearch/quicksearch", { winQuickSearch.show(); txtQuickSearch.focus(); txtQuickSearch.select(); + + if (corrected) { + if (typeof corrected.right != "undefined") + winQuickSearch.$ext.style.right = corrected.right + "px"; + } //Animate apf.tween.single(winQuickSearch, { type : "top", anim : apf.tween.easeInOutCubic, from : -27, - to : 2, + to : (corrected && typeof corrected.top != "undefined") ? corrected.top : 2, steps : 8, interval : 10, control : (this.control = {}), diff --git a/client/ext/splitview/grids.js b/client/ext/splitview/grids.js index 9557c3798cf..dcf192e30b2 100644 --- a/client/ext/splitview/grids.js +++ b/client/ext/splitview/grids.js @@ -256,6 +256,18 @@ function createGridNodes(name) { blueprint.node = blueprint.struct.node; if (!blueprint.splitters) blueprint.splitters = splitters; + + if (!blueprint.node.$resizeListener) { + var timeout, lastEvent; + blueprint.node.addEventListener("resize", blueprint.node.$resizeListener = function(e) { + lastEvent = e; + if (timeout) + return; + if (exports.onresize) + exports.onresize(lastEvent, this); + timeout = setTimeout(function(){ timeout = null; }, 100); + }); + } } function insertEditorAt(parent, editor, insertPoint) { diff --git a/client/ext/splitview/splits.js b/client/ext/splitview/splits.js index 6c8dea80bd1..f92a8ef6c69 100644 --- a/client/ext/splitview/splits.js +++ b/client/ext/splitview/splits.js @@ -43,6 +43,40 @@ exports.init = function(splitView) { } }); }); + + ide.addEventListener("ext.quicksearch.correctpos", function(e) { + e.returnValue = correctQuickSearchDialog(); + }); + + ide.addEventListener("ext.gotoline.correctpos", function(e) { + e.returnValue = correctGotoLineDialog(e); + }); + + Grids.onresize = function(e, node) { + var correct; + if (searchWindow && searchWindow.visible) { + correct = correctQuickSearchDialog(); + if (!correct) + return; + if (typeof correct.top != "undefined") + searchWindow.$ext.style.top = correct.top + "px"; + if (typeof correct.right != "undefined") + searchWindow.$ext.style.right = correct.right + "px"; + } + if (gotoLineWindow && gotoLineWindow.visible) { + var ace = Editors.currentEditor.amlEditor.$editor; + var cursor = ace.getCursorPosition(); + var pos = ace.renderer.textToScreenCoordinates(cursor.row, cursor.column); + correct = correctGotoLineDialog({ pos:pos }); + if (!correct) + return; + if (typeof correct.top != "undefined") + gotoLineWindow.$ext.style.top = correct.top + "px"; + if (typeof correct.left != "undefined") + gotoLineWindow.$ext.style.left = correct.left + "px"; + } + }; + return this; }; @@ -392,4 +426,68 @@ function clearSplitViewStyles(splitOrPage) { }); } +var searchWindow, gotoLineWindow, searchPos; + +function correctQuickSearchDialog() { + var editor = Editors.currentEditor.amlEditor; + var pos = !ActiveSplit ? -1 : ActiveSplit.editors.indexOf(editor); + if (pos == -1) + return; + + var parent = editor.parentNode; + var editorPos = apf.getAbsolutePosition(editor.$ext, parent.$ext); + var editorDims = { + width: editor.$ext.offsetWidth, + height: editor.$ext.offsetHeight + } + var parentDims = { + width: parent.$ext.offsetWidth, + height: parent.$ext.offsetHeight + }; + + if (!searchWindow && self["winQuickSearch"]) { + searchWindow = self["winQuickSearch"]; + searchPos = apf.getStyle(searchWindow.$ext, "right"); + if (searchPos == "auto") + searchPos = "30px"; + } + if (searchWindow) { + var right = parentDims.width - editorPos[0] - editorDims.width + 30; + var top = editorPos[1]; + //console.log("editorPos", editorPos,"editorDims",JSON.stringify(editorDims),"parentDims",JSON.stringify(parentDims),"right",right,"top",top); + return { + right: Math.max(right, 30), + top: Math.max(top, 0) + }; + } +} + +function correctGotoLineDialog(e) { + var editor = Editors.currentEditor.amlEditor; + var pos = !ActiveSplit ? -1 : ActiveSplit.editors.indexOf(editor); + if (pos == -1) + return; + + var parent = editor.parentNode; + var editorPos = apf.getAbsolutePosition(editor.$ext, parent.$ext); + var editorDims = { + width: editor.$ext.offsetWidth, + height: editor.$ext.offsetHeight + } + + if (!gotoLineWindow && self["winGotoLine"]) + gotoLineWindow = self["winGotoLine"]; + + if (gotoLineWindow) { + var pos = e.pos; + var maxTop = editorPos[1] + editorDims.height - 100; + var left = editorPos[0]; + var top = Math.min(maxTop, pos.pageY - 70); + return { + top: top, + left: Math.max(left, 0) + }; + } +} + }); \ No newline at end of file diff --git a/client/ext/splitview/splitview.js b/client/ext/splitview/splitview.js index faac8c1cab5..39125e7c3cb 100644 --- a/client/ext/splitview/splitview.js +++ b/client/ext/splitview/splitview.js @@ -486,6 +486,8 @@ module.exports = ext.register("ext/splitview/splitview", { if (active) { tabs.set(active.pages[0]); Splits.update(active); + mnuSplitAlign.setAttribute("checked", active.gridLayout == "3rows"); + mnuCloneView.setAttribute("checked", !!active.clone); } else tabs.set(activePage); diff --git a/client/ext/zen/skin.xml b/client/ext/zen/skin.xml index 5280b63b0d3..8037182926e 100644 --- a/client/ext/zen/skin.xml +++ b/client/ext/zen/skin.xml @@ -6,13 +6,15 @@ height : 23px; overflow : hidden; cursor : default; - position : relative; + position : absolute; -moz-user-select : none; -khtml-user-select : none; user-select : none; line-height : 17px; opacity : 0.01; z-index: 2; + top: 10px; + right: 10px; } .zenbutton.full { diff --git a/client/ext/zen/zen.js b/client/ext/zen/zen.js index 18a9f7e33a4..8f2a1f85a04 100644 --- a/client/ext/zen/zen.js +++ b/client/ext/zen/zen.js @@ -91,9 +91,20 @@ module.exports = ext.register("ext/zen/zen", { this.setupHandleListeners(); - var editor = editors.currentEditor; - if (editor && editor.amlEditor) - editor.amlEditor.parentNode.appendChild(btnZenFullscreen); + var button = btnZenFullscreen; + //var editor = editors.currentEditor; + //if (editor && editor.amlEditor) + // editor.amlEditor.parentNode.appendChild(button); + var page = tabEditors && tabEditors.getPage(); + if (page.fake) + page = page.relPage; + page.appendChild(button); + ide.addEventListener("editorswitch", function(e) { + var page = e.nextPage ? e.nextPage.fake ? e.nextPage.relPage : e.nextPage : null; + if (page && button.parentNode != page) + page.appendChild(button); + }); + vbMain.parentNode.appendChild(new apf.vbox({ anchors: "0 0 0 0", From 91672f4be2276366acaf2d3fe854b00b3b8d3582 Mon Sep 17 00:00:00 2001 From: mikedeboer Date: Mon, 19 Dec 2011 10:56:03 +0100 Subject: [PATCH 09/69] save hotkey (quicksave) now works correctly. Fixes #396 --- client/ext/save/save.js | 4 ++++ client/ext/splitview/splitview.js | 11 +++++++++++ 2 files changed, 15 insertions(+) diff --git a/client/ext/save/save.js b/client/ext/save/save.js index 991ac66de87..f20455f8f70 100644 --- a/client/ext/save/save.js +++ b/client/ext/save/save.js @@ -224,6 +224,10 @@ module.exports = ext.register("ext/save/save", { if (!page) return; + var corrected = ide.dispatchEvent("correctactivepage", {page: page}); + if (corrected) + page = corrected; + var doc = page.$doc; var node = doc.getNode(); diff --git a/client/ext/splitview/splitview.js b/client/ext/splitview/splitview.js index 39125e7c3cb..91ab05fe020 100644 --- a/client/ext/splitview/splitview.js +++ b/client/ext/splitview/splitview.js @@ -80,6 +80,17 @@ module.exports = ext.register("ext/splitview/splitview", { _self.onCycleTab(e); }); + ide.addEventListener("correctactivepage", function(e) { + var split = Splits.getActive(); + var editor = Editors.currentEditor && Editors.currentEditor.amlEditor; + if (!split || !editor) + return; + var idx = split.editors.indexOf(editor); + if (idx == -1) + return; + e.returnValue = split.pages[idx]; + }); + tabEditors.addEventListener("tabselectclick", function(e) { return _self.onTabClick(e); }); From 4ed8431a0637f3b80d72e40173b3270d62f3cd95 Mon Sep 17 00:00:00 2001 From: mikedeboer Date: Mon, 19 Dec 2011 11:26:12 +0100 Subject: [PATCH 10/69] line on top of editors narrowed down to 1px and tabs now look more merged. Fixes #398 --- client/ext/splitview/splitview.css | 5 +++++ client/style/images/editor_tab.png | Bin 5791 -> 4148 bytes client/style/images/editor_tab.psd | Bin 81996 -> 84913 bytes client/style/skins.xml | 2 +- 4 files changed, 6 insertions(+), 1 deletion(-) diff --git a/client/ext/splitview/splitview.css b/client/ext/splitview/splitview.css index b927562e18b..e3e2d55355f 100644 --- a/client/ext/splitview/splitview.css +++ b/client/ext/splitview/splitview.css @@ -5,4 +5,9 @@ .editor_tab .btnsesssioncontainer div.curbtn.splitview_inactive .tab_middle { +} + +.editor_tab .btnsesssioncontainer div.curbtn.splitview_active .tab_left, +.editor_tab .btnsesssioncontainer div.curbtn.splitview_inactive .tab_left { + background-position: 0 -234px; } \ No newline at end of file diff --git a/client/style/images/editor_tab.png b/client/style/images/editor_tab.png index 3f8791c89306936686363cee6843c957082a8e73..0cd67f79c04d0f3ed769aec754c3b532134fb1a1 100644 GIT binary patch literal 4148 zcmbVPXH=6}w@xUbOA!M?#5RyOw1Sz3KP;vGyxg9ARtvhrASjjim32~ah!X9%=+$H_g!bLQ{MeNyPUnxIw#5A_K1Y2q9_0W zkg&2ecjWc50059*7{nW)VJaECPJwCR%5s03fKBbh*oINd@v z008)XD9)}-R~u`6B8{p^*p|@@rv~%T0DysUcrbw&KxTry$-a~zL+Hn+wNNmHWC%Tu zwn5kgo00t}mXUO_Q>3jkF*1ONAwi9ezy{&^JOV12NdSja1A`d);fBz^c=dVX?PWL= z{Fewbz!3V=C|4VMuo;a`2BS3*Fd{++0oFlkBDGPvy1E)*Ed&w)M`*#3S}>%pzP6S= z0s;Q}gYvY|Nk00H=D5Fg@n(ilKPEF+9}W)-3)2kK)}+yW;YbVyv(2HU1>;G;7!g5C zLO3jlvHur?IhjGEQ-YZkS`c`fk>E`WVH!etmi}~s8f;_p4{;FV??CZF1`j6$!;zW@ zIF-8X*I&{MrX%@(%=lMnhI2$P8SY4C&_d`$-hTM({{`m7?!PPA7UWr@Z%3!_HiZyq zP9ug;$w5pjb3-Wak0yyi($}^yMd@i_v=F?eqlZN5VGw%e7&DBnj+r(ZjYI$9_*X2> z9EZT@XlZGoa7d&DQqKa3Hba=}>FOc1O%OQ5FRWD%gGmS?l7H!?Z0r4rMgCW;z8RfN zVAAN$G+N-V46yg3F=-4xS}@qm2@SR(5Gg_1^X={V>8&}LP6;KGaC90K{8xPSDgUA$ zrS*UL{S!<2|HKT=GX}n$9RDj>el77baC`Vq>hmW56c0IwS21*6X<#1S9|i!#Nmk}2 z&f!Dz9sz-}ujOjWC2LENm~WM7a1)`(L>aZKwR%ukES zKqT%s#@>5yNCToG%x57W2*Ju8+H}TUhqb%HqgvZH8}HXqZo^ zry4H2FlSI8|XpGj?`%yU%rKq=^+y z%yj2uOG--Cjz`>Pn?mZ_`E-smtg32V=VP$%BG|RO~^&)s#2X3AV(Zm^?ur0mRNi zzt#Cc#sZ(l;nQy$ML%Wk?;hwAbFJ~~hF-m$#1TT!Myo<@I_27SSoL~TvnByE-G^;X z>hEIpw6?ZJL=JoaY7A8bq#lI9U}m~z>%@+!V$bR!qMt%*ON+y|tUZBvH@86#GnSyB z-~(;q@MEemLq99XjHT_ z7rPmin?E4MpC%ewb@#4{P`|->b!@t1?%AyN&Q8y_p-)dn3elCkVq#)s=EnpBeA=9) zK&P+0(|c9g*R$(zyws;??{_cUl2$Jmq2{7ou$dw?0_>eOw`Y_HnMTWLR&? zU^@_aZESGwPWimkSk+NLoWpT!bUlKT5z4CKn-#P6eJL@MXb4f570rWlI=%evdkNcX z_wKg$#a7qv!lHT=N*LSDA@+D{^|zj^t0Ndb}Tq!x{}qMTI&-@ z_(l}LdTMH_(>t;;Z4!KL!;HaLhKq@crrg1E?;e-G5-H{tulS9>u>9s1(FT^Ao10ZY ze_=lC(4nD1FkC7%UC-IYWl~9AUQc*mE#Qoj9PZo3hBf(COs(o;GRV9OE&%+#x#Hp6G^QUGgPgoi7h zS4NG#&%9&~pyOcWqB8;|{8DY18ynB^yRwbT8xeANt{nvKF6Q<8LPEh+3f6VM@xsiE z>)e{8%vZXPPW+G2Xu$H4ii>w`MKAH`TBABsv}b>NLM zo$~x6>dpEpJti3}L&NG}fOBARf<-t;MJp%ym9o?0(2$UuIj0(H|)K zNRL+qO7lgd#*ga`Ey$g1_dL`CPtI2Ko4Fn0bF;G=f;R`+O^zPDM@{;1iB00Ybpmt>eR-S-Vq2b{3KNS-?9vDcy5mJ-)NNse8VK!xBPC#h7V%5^O^yHH?PX9xyGc-ZKx`sf!Dd})QB6jtSEZ)vbdF+Fb+Ei#!jA^H^C~ z*(c~*&hWyf)Q+vQF06a)6W=S#yYnrhqpOwGB{n`wsI0`rG5kNE0-`_oY;`zBJ5I2q&WF%AY*67HHN7(1LO&|ff zo|3`A+2i*C+I2_ar)r+vDm6tAnow=Qo0nbmny5z7~s(g=DY8c%a_avYcT5LMfxE+xrpht3a_rr}fwifuh=ny zk08koeh_S@QYh-@Mh@)%-7GJ2Z=GpcIpbbjL9aAU{?659PEdZbg!ET?E)FQRwm3Q$ zhn3AeQ2tr|3_9UhZ(Gyr;*%#-;#x9(6Y4Vzys)+~e0*W;+w!0k#1Z?dhuQ~ls`EKn z9T`$us4`A+)7;&L7nAz2@#FFy-5N9kVV_ulJeCzP_{UxUiP!gnJxa-KDL0FXiUP(N z!df7qH!6_F&f%JI2NZWrEPQ^~eDMiw%#-nBV?}>v5JGB}XWz2TjoRT4xlJg>Y8=k_Z&ahuZS;@>)$eyf*Cp zpVwpSDs4-)(z4!-+`=$6o9Ko|936Y?Po5lYYB(=>D&ZX*Ij5yz{(f@u2d;Gna_(5w znYEw@U*Cf#EBxh*a(k%q2wmN^uPXj~d9h|V# zIEwfS06AS9F0ym`JYXrta}P{UPt&dxUASAu&CT`oiHwZwdKR%m(#RlK z@>fUimYjZ82ETMR$xc}Q#LZ>VA-*oudD8~*Ne%K4Uv{tb*S#5*}oAF_lrn>JQ?cPp_i{J12lNu*XMG2d8&iUyHThMh!xymEWK~} zi2ZnZwnX|*t#vPiyr=*lWL+u2Yqh$+AOCu3vaPwNr)RHBrekH2u8G1}&7`i0RgBB^ zFE7?IIR>FMBBI$bncQg``wUgVjh5-<38&&4wzmx`FBD-j?DifYB}#tHGK#Kx{NRCq z{_UhX%1T-Imd(S%7>q(TWi$VF<;NcX;&Fo~d(vV3Pp|puRMyt+gKt(#vIdu?4_b$b zUWzItq#V7pk~W*k0fTZ$B+@hfSdv;ZK6KLZ*U+IBfRsybQWXdwQbLP>6pAqfylh#{fb6;Z(vMMVS~$e@S=j*ftg6;Uhf59&ghTmgWD0l;*T zI709Y^p6lP1rIRMx#05C~cW=H_Aw*bJ-5DT&Z2n+x)QHX^p z00esgV8|mQcmRZ%02D^@S3L16t`O%c004NIvOKvYIYoh62rY33S640`D9%Y2D-rV&neh&#Q1i z007~1e$oCcFS8neI|hJl{-P!B1ZZ9hpmq0)X0i`JwE&>$+E?>%_LC6RbVIkUx0b+_+BaR3cnT7Zv!AJxW zizFb)h!jyGOOZ85F;a?DAXP{m@;!0_IfqH8(HlgRxt7s3}k3K`kFu>>-2Q$QMFfPW!La{h336o>X zu_CMttHv6zR;&ZNiS=X8v3CR#fknUxHUxJ0uoBa_M6WNWeqIg~6QE69c9o#eyhGvpiOA@W-aonk<7r1(?fC{oI5N*U!4 zfg=2N-7=cNnjjOr{yriy6mMFgG#l znCF=fnQv8CDz++o6_Lscl}eQ+l^ZHARH>?_s@|##Rr6KLRFA1%Q+=*RRWnoLsR`7U zt5vFIcfW3@?wFpwUVxrVZ>QdQz32KIeJ}k~{cZZE^+ya? z2D1z#2HOnI7(B%_ac?{wFUQ;QQA1tBKtrWrm0_3Rgps+?Jfqb{jYbcQX~taRB;#$y zZN{S}1|}gUOHJxc?wV3fxuz+mJ4`!F$IZ;mqRrNsHJd##*D~ju=bP7?-?v~|cv>vB zsJ6IeNwVZxrdjT`yl#bBIa#GxRa#xMMy;K#CDyyGyQdMSxlWT#tDe?p!?5wT$+oGt z8L;Kp2HUQ-ZMJ=3XJQv;x5ci*?vuTfeY$;({XGW_huIFR9a(?@3)XSs8O^N5RyOM=TTmp(3=8^+zpz2r)C z^>JO{deZfso3oq3?Wo(Y?l$ge?uXo;%ru`Vo>?<<(8I_>;8Eq#KMS9gFl*neeosSB zfoHYnBQIkwkyowPu(zdms`p{<7e4kra-ZWq<2*OsGTvEV%s0Td$hXT+!*8Bnh2KMe zBmZRodjHV?r+_5^X9J0WL4jKW`}lf%A-|44I@@LTvf1rHjG(ze6+w@Jt%Bvjts!X0 z?2xS?_ve_-kiKB_KiJlZ$9G`c^=E@oNG)mWWaNo-3TIW8)$Hg0Ub-~8?KhvJ>$ z3*&nim@mj(aCxE5!t{lw7O5^0EIO7zOo&c6l<+|iDySBWCGrz@C5{St!X3hAA}`T4 z(TLbXTq+(;@<=L8dXnssyft|w#WSTW<++3>sgS%(4NTpeI-VAqb|7ssJvzNHgOZVu zaYCvgO_R1~>SyL=cFU|~g|hy|Zi}}s9+d~lYqOB71z9Z$wnC=pR9Yz4DhIM>Wmjgu z&56o6maCpC&F##y%G;1PobR9i?GnNg;gYtchD%p19a!eQtZF&3JaKv33gZ<8D~47E ztUS1iwkmDaPpj=$m#%)jCVEY4fnLGNg2A-`YwHVD3gv};>)hAvT~AmqS>Lr``i7kw zJ{5_It`yrBmlc25DBO7E8;5VoznR>Ww5hAaxn$2~(q`%A-YuS64wkBy=9dm`4cXeX z4c}I@?e+FW+b@^RDBHV(wnMq2zdX3SWv9u`%{xC-q*U}&`cyXV(%rRT*Z6MH?i+i& z_B8C(+grT%{XWUQ+f@NoP1R=AW&26{v-dx)iK^-Nmiuj8txj!m?Z*Ss1N{dh4z}01 z)YTo*JycSU)+_5r4#yw9{+;i4Ee$peRgIj+;v;ZGdF1K$3E%e~4LaI(jC-u%2h$&R z9cLXcYC@Xwnns&bn)_Q~Te?roKGD|d-g^8;+aC{{G(1^(O7m37Y1-+6)01cN&y1aw zoqc{T`P^XJqPBbIW6s}d4{z_f5Om?vMgNQEJG?v2T=KYd^0M3I6IZxbny)%vZR&LD zJpPl@Psh8QyPB@KTx+@RdcC!KX7}kEo;S|j^u2lU7XQ}Oo;f|;z4Ll+_r>@1-xl3| zawq-H%e&ckC+@AhPrP6BKT#_XdT7&;F71j}Joy zkC~6lh7E@6o;W@^IpRNZ{ptLtL(gQ-CY~4mqW;US7Zxvm_|@yz&e53Bp_lTPlfP|z zrTyx_>lv@x#=^!PzR7qqF<$gm`|ZJZ+;<)Cqu&ot2z=0000WV@Og>004R=004l4008;_004mL004C`008P>0026e000+nl3&F} z000ZhNkln9U4;BwS)pe8Yz*gEki`2 z>R_cBlu}h~*xCt-iuz$6CQa4UNT?EY(p1r?QPfm4o%&^>Qd7`?vC_CvTgRy#NaB!S zJC4tJ_u-!FTsyXNuYJ){!Hr%yw&kCn^XH!TJ&=Sliy-?lgLu+6^oK)pd1s8;qzk0<1i8YnRj1GE`h# zob0_U0IO?iYSx(omX(zyJu?m-JornS&6Z^fSYBS9?7iomPUmLREANUGD-yl_OhZFM zTVY{gmT6#RW#zny4B+*L9(u@@R&Iy`<^cG|s#U9s9S(<*7EsrR1pxkDR8+J%D=W)x zbRy3?6MQ~jHh^b|=r0!JXhd_0*XvEZGL)8@2EGUj50*)`PdF&)c79Tz#RBhu!h21= zHz@ZM{y!8du=uzKya!wiK)b$ru&NY!5r7ZCFn|HC*E^jNk%-9S@wj_?dp`#7yz~*6 ziOPZzrX(}08f~j*g^;-i>9Wg&4&*kejGrBB|sqr z_U_&L2!M^2C8?^as&xP=rSCAAu|gtJ&YnHH4M2^dCMO#deKa;UIsw#L(uYC_6ciK` zx3{;~8Sw`VDI_Aax3?QoX-J_E0`>Lv^#Il^v~UdbJetvC!em=Cw$nLAEs#;f4QnFh55wjWR6r0VKb?n%&UzL@WJz7>)hD3ga=_J+A z(9lp`Uj9;fc{vjK6{dlOg@xI>ckkX>Sy}l^0(-%NU(*4_Z{H@4_& zZ2O6mr!oS(_Qy_~IC<&;07n5lX3tQN?ZnAb$HU?9QLopRGd(?pOn^B=^sdL_Y4du0 zAR;iv(gzA5-gUWLZGQhG^vhYggYwFt%hfh9;a8Vi#-fo9u-4=89G@7U02si)Vwk3Z zfk5yrw|h8SgGEUy)-sG9-7x%vz;7X6jvn`QZNa+AW zq0k<`e?t1Q1OfphB#uBNKqQOuHzkDH#OSEbsbdL%*#Le&J3IT@rAwEV`X?rm&NH=n zOyAm=<%=DE{f&$heSDKMDsJK{5BnDvFIijLB-(3a2*e^+Xt7iELvZI7e~nFKwRJCO zarfaJ0AjKCq4ihI2#Z`H-G!GjiCYZhmd`_r6}GgrWRz6mD+jF{9RToW01srmVt+m` zfNR&TIluePeKZ^n4+1y|z?L?UF$IA@5I&y|moHz*86F<~0};Ik^;Vg5fOCEU5n*KH zdkhW^wh1BLO&f@q0465<7<9SX0KAe`*3+@%wjY=npMb~XISzw{xK1JFwlR{dLEY}* z>_8y+mPv&PrlI^20%2(A>&E~z8wTo=R3aZ?^42Z*{gW@HRXHR^1JY|)X;7hv`UP`) zf1!E`@++G9+i^a`Sng4E5R-dhla(b3TkQ+*iq{q-H7TiK-N;4}o(n-Fu) z=WGfB5DW$}H8s_-Fc1Rz0EFCWt3e=useS76dG{=|a3~~wmuOJz^QhNAfY<9QCZcUA zfx3%X$wTc6%+7`}K0e-&8mJDe3<4MW=kdYtz1Q0Y^*;903e`{*sfx9bz82K?d_G?e z=X_s^K1_5iPlc;rAfo*WB5=;(@r)cysgPvomCgqfgzQtd+g%Ibz=BPtXmI&RAR*LK zk<>>JVS0KB!^6X;02C(}6pncz1O%y}suU1J>T~&b{j2NC!r|}-Cr+Nqi3M_kJ~$_E z*<>Mv`hRqw5RGtn7GGSv*u**SJ#q5XvgkV1wD}i_YZ1~rgvpP>!a~&7*H30;Wt{-< zS3419R5|qabjOw=J9qB1qNK?I@F0M~OfS$dsMkwZ9XN2H`P#K>)tOwaumRZk^2;yp z{OF^Pc80^@TmT{y;KrjzkM8>L!w;Wij1gmuGsY-gVAZi>$6EUO`dS%dTu~IkRR1b1 zU><<#^XJcRk*Wk^OaP#C6>jM0=-3em1eQt|moTKGu;iV0-dR5|Fz~3NC|s{hhG~o7 z!S3$vjT%U&4CyGm4?vO2<*L*AO{&sW2f9SjEZ=4y!KVFXyw*VngBo4APv!i+*YfYQOi!8+X((N7gJ3#{nu>~wO@=efyp zKE_`SVGE7&%1=N2v{8fUnyZHzo;E9Aef8BQzu&)HP7!DcEtPMZ4EQ4et3UbVlTErU z0n=5vEM9FYpaS4$`}Xa7d~9s2LQxbUOJ|{*my|61+7moqyMO=w?H4Xw*bRUSArwWC zTSCTYp&us10B_#Bx#_v*o@@T>v(H*M=K)1ggjC3FHkLd07C%&o9;Qz z)~s3cZ&~Xp+1c5FmX?;jH{X1-55VUDOy_J{X=&;2w2Gc{p8e&QU)Bx{4bhGrJEo!j zX2l$^q@-lOZUapK;BvWYMn*%j zLNg`;fxyy^j*cAwHkfMi^5x5)iQg4z+Xj!vv%=wUcs6X<@C|_5W)|3a-hsA-?e6a0 zD19x{IfWRD-rcEaRhi4>ssm6YJ%`&y70y$K=&gl>Lx&D+0#Ke#PKnvmX;W!nV8983 z7ZK;Jg}T1fREo*T$&#L)o>fqP$-W>kepfCMcnd;$4i%;q>Q(Bz1-f$ONsOx zkcXhD4uwMZb#--B0Vqi~(UZ<717nQ(`uY}Zb}}F(7#xz`LusmwyOPK^NRk?eb8heK z>~yA5ne+!Qaf1{+o1_P^GSve6+hi)GQYKe)27rY^p*)|@H{IOa>;~XZq%dg+A(S{E z05IuAtOl@T9#9C8G$c$9RRFNDv9XG$pMLsL0Cn*LYbz=$dRD%eEST03O#lGqcDvVJ zzI-{*+S;n6U;zNO`|rR1H=Oes0}+~;nGumlgondnJ~J~D85$b;>4ggy>|3^Mna$13 d{SE;BHvs6mhwXXbw?_Z~002ovPDHLkV1k*-^r6gWN>0hMiyWif^Z6? z2~Ak0iC78=O9D-afe0`OgcuBjLTTWHLk*>@R**1YEP0V^OVfKt7C|CDJ@n9$KRS}; zd+*+P@7>?K@6C+X`AszyF7H~{>D=<6!_!p?u&@W$?Mj>-J<7ZvaIFUu5tG2|O?cmK;(Os0Eywlkr`yP|`guF)>Y{NbDPz7$4WKze16uh)YTv7MGULr(Z&9T4H=k??P36 zMn>|;^&xGHsR73W_zz{PI{&*?{MEq3P_GF1j#53}Sr%kG8FDi|Okx#P1OW)*Vca?> zmbH?IB1kDfYXH8ev`vg(oLH4H&*7ehUZOQXoXhUUkRhWb0bCv0r2Lq-+IFJZl+MmB zP-bSUloN(enSgbFmhD_y;Gn!2C=A(bXyhrQ7Vo;(Ip-BZDGvnD~HYe*g+J)(`C3gqBGG_|I9YT^d1!ZRIau+Gb>KA6uMWgtfNzLorIZvaW zhjj`H(zR-@`nb8?{s~%j!EB8tk46s4$<0%yX|$?rol?C}MV7S37OeS?$xM%@P=j*S zI;A!*dyaB0wWa(+h+318t_(`d1z zY96AMkC~cfYScu>8MXPO)L2Y`e3Y2vB{$U#Z)C=<{3X&E$NvSQ! z)u=r!-A%Wn2cIUYg1(E996h31?cl$kw#YAW{_zt2*VC7|3%94ovYotqMvU}K3c#5# zq28E0V>}BuQWmsnH}JAqQO12Uo9myRGRkJ13D-w_bwhqKWfS<4LA!$n`C82w2rpPH z*9BaMV8uB37$PfrmQ1e)btU&rTVpgHv0rePuvhYY7zhd?t|9v~OF zGI@)KdXkNSc7IF%;`8fMwrCGa% z_pj<(UOMr10sAm1@ycA!fCL^g{)Ib!Iw?=l9Hr!t>y)Xx5#- zKVw^$BW0$QsU5t=5ZmHuaD=;Le55so>*MoJhpxa>`I3QRzCLserYg2uR%l;LRZK;4 zXge`gUD}p~$}v^GWDxn~g%A^_ins<-MJyCkg(bEDQ$-w(scI&r3X5tf|U1u;ePco7W9K_GQ z0yi*KPmup|u02HteN-hdrFf1=N$hzdLM+t=Ck-Li99CWQkY9!9aYeU$$?rQ6%a?6DA?f$Ll+mT+(Tri<6ae6Fn6EZ!=m5qBU)rzmJ#7V$C2(3V0F((3c-tv!i1tm zIfV_%^_xisjO?)h+n_{fJL85IqsV}+Q~QEDy)wX;{QU)kcxCZ+amGFmGluT{U2FaF zRV#zn$vy*LGSDv@5Ii$wE-Xoj3LgAjHE11<;F#BzVgCza-wbY_eDo}Ln6pLXCx$z2k846{j%Z1_u%GC> zJ9eo%1n03?qbUS0G78gq@K-}9Z0Ckkef<5J6uN3o3TYx|o%ItL;(FQbu^s7l zo-DQ_vxw8{A** zB(kuBcEWh8U2ve)=pwSz%6g#zSkS>b!c=RW&qrDd3XhS{zgH}6g@qE@{!}B9Y zbpuF*s*q3YRSo#BmL06H>mQ;Z!Bls@5e;y!uCftdB3%}^XSbj>;g-u{H#Y{ct9NV7 zO`Vuc@L5qI+P_cdnM``on~CYxC}z+E;~Sk1ykNyVMJ7MuBj z1y@FkBK4^0zpt`ok^29M%9cgypJOgtR@t)1-p`oJl4u!g_w$m(PAr0J>t3dw{p4Pr zQbSfJ_DzW+n)P5UU+^`*rh0@y(}e{X2K|CX7-D1C0Yh*X7V+av_|o!3uX`n-#MDKS(ekFzrYTdmXql!& z8Kq{pq{UWY=HD_1Sz5G=D5~k&D(-#%=e##F(f|K`KJWRS^E~f!?(g~CcgaiV+}OmK zXY1nS4G5Hn3dS>h>VO)sm7%3VTNe*c6%%Hqsf_(QGC5q?dL*#mDUO~ET!9>O^^UFd$E|kH6_I^1_o;A-N9t0 zF&QqSRYTh2GM_!2?SW%lCaxTm+5t2m9Kv7)#DN%MAqX=VLL||iqYeIPl@#dUlaweA z$+w;#Nlu1nBbFr5{Yw@+lU#FGU`VgcU4^7GJ=XIn4Mqu{T+h+N1My9W&tv$U3Sh7a zKxGj00MSkKbKpCNi9^DJTs`sn1qhiHgyC!O`VJ%mUrkL-U5&4<&KL1Ccp}|VeEuk1 zJ#B4WZEZafpE=$^x!_apI0Pi%@dQGFrckJ+T&xAEV(*+GV)wLHP&5pIb?=a?B-fmRg=Bme?NQ`y4rg5 z@%-;iKa==`xn0SOvl(-1@uP5=H2G>BwdQfR`-FX2TMG79)c@6UqOh`|t=D&X{MNLS z=c;Z$>6^} zqatvE-j8=4b~C#mb4BKMjsmglr<}K=yKMG0M##oJoEBA9(|GC4MXG$j$wQ(2rqZGP z<+O37EaQwvh&QFKjDYCaA34wv>(PpwL?(-6OEpqYlOI`Jg& zq)BvbPh!Q_EwLZzwO42L+52~BJy|F3DiW8~$*Zg1uA90ctxUWB)CGgC6PJ(vX=CWu z+q70X*wJ)m!^yII`RSkqb-nBRs(SX(K;P=A&M)iVoZJ1q=MPWg@A+SvL{+s|?C2kx z{oJhO@{69ezWp>jh`b*kZfooJ{b-=HAS%D#2c*!jITX=r0doSB`ecuZ9! z2A!s%GAo;|>!6`(y4dRyr^FyTI6Ul<(anvE3u>d9TADVvH#}=xa(Yc#lYY_+0<*N;9QS4A}%b_Lo!3l|UgcK2P4Xqg?TIQKG$k{4g3 z;f$02Tc@SRiyf#{xfT|#Thg}$x0O_{rVdBs?YAzpZpGXF&~%|SKQ3snM67r$yX8$c z{=R7+C)Y7YzGD1b&+k;d+|IY-`$UU|LqT7c$?|CEdu}*Tv-c=nV^+UAFJj)ahGpq{ zl74!)@YIIf!qyv0yr~%tRL9ijfnFLi1CJFuzepSKh%&1>6aANMWFr-Ry)NqQyjfH=R3)RfQ`yYNZ=HdA=w>htaf!Gz?DJzwe(`3H{$yHu)c}&m+S~0D>Yw6 z)_$L`_oqpB1pgtT3_|t@D;y5NNFw;iQ6T(L>;>9hSA=og`@j#475e=g1uW1Al?cCF znezeh3tg>TXtK_nP*6Si8N>5@H(Y}wtR^mg-D7UCKQcu8Qe>v%4oC>mh0sF`m#05a zW{^&KD2c#e%~^)o3lo$QU}NApB4HR2kqK+2^$^+MUztSO0EYm7T!UK#wbaUC)F%O) z@-}4|etGtNO1_C}`_$h@z?qc-_PvJhck?=cgBcUl{Bc9Rq*ha!)Y$IU=V{MOxAKB)dl9(J1xTs=8Sm>l*9el1-TiHJsfykEad|g1=kcofXiED6+vRPMN52HQ_;FL{a&!VJ@5N_Z|IsxpscxH{}NWd<4T zn!!ZWk8C7_f^i;739xgjK9Mkth{%Mb0U?nM;d>90Ho#+Y46eZ~g6`7RVbmu9oGRbN zGh8tHeM+%^#Lbv z4Q^5X`Hxz|IFaM39rp z!cXBfIG$02pF%%4To8eu!Zy7B!Gi6Ol=0~YM3DMbA$|%^F=U&?5#pyX9@pTRBIalo z!*TU9hDaR4sh$ejong>56^4ApJY}Z)lIcwn>CT*~$I!Frky1jpaix?<7)C^7b{p)= zCbDJpR4dYk>`?^|*WeaqXZsR{aV!boRMyAx^BuQGl5s5J4smQ^aEYzd>~5!d`c8c` zt7xe%ic}$^SX6Yw6pdj?qPuj`&8DE;MMaa4B_hiry!-`8z3 Date: Mon, 19 Dec 2011 12:19:32 +0100 Subject: [PATCH 11/69] updated apf, which fixes #399 --- client/js/apf_release.js | 91904 ++++++++++++++++++++++++++++++------- support/apf | 2 +- 2 files changed, 76412 insertions(+), 15494 deletions(-) diff --git a/client/js/apf_release.js b/client/js/apf_release.js index d826f7ff6f1..42026001f28 100644 --- a/client/js/apf_release.js +++ b/client/js/apf_release.js @@ -1,15493 +1,76411 @@ -var apf={VERSION:"3.0beta",CDN:"",READY:false,NODE_HIDDEN:101,NODE_VISIBLE:102,NODE_O3:103,KEYBOARD:2,KEYBOARD_MOUSE:true,SUCCESS:1,TIMEOUT:2,ERROR:3,OFFLINE:4,debug:false,includeStack:[],initialized:false,AppModules:[],autoLoadSkin:false,started:false,crypto:{},config:{},_GET:{},$asyncObjects:{"apf.oHttp":1,"apf.ajax":1},basePath:"",ns:{apf:"http://ajax.org/2005/aml",aml:"http://ajax.org/2005/aml",xsd:"http://www.w3.org/2001/XMLSchema",xhtml:"http://www.w3.org/1999/xhtml",xslt:"http://www.w3.org/1999/XSL/Transform",xforms:"http://www.w3.org/2002/xforms",ev:"http://www.w3.org/2001/xml-events"},xPathAxis:{self:1,"following-sibling":1,ancestor:1},hasRequireJS:typeof requirejs!=="undefined",availHTTP:[],releaseHTTP:function(http){if(apf.brokenHttpAbort){return; -}if(self.XMLHttpRequestUnSafe&&http.constructor==XMLHttpRequestUnSafe){return;}http.onreadystatechange=function(){}; -http.abort();this.availHTTP.push(http);},browserDetect:function(){if(this.$bdetect){return; -}var Browser=this.$bdetect=(function(){var ua=navigator.userAgent.toLowerCase(),platform=navigator.platform.toLowerCase(),UA=ua.match(/(opera|ie|firefox|chrome|version)[\s\/:]([\w\d\.]+)?.*?(safari|version[\s\/:]([\w\d\.]+)|$)/)||[null,"unknown",0],mode=UA[1]=="ie"&&document.documentMode; -var b={name:(UA[1]=="version")?UA[3]:UA[1],version:mode||parseFloat((UA[1]=="opera"&&UA[4])?UA[4]:UA[2]),Platform:{name:ua.match(/ip(?:ad|od|hone)/)?"ios":(ua.match(/(?:webos|android)/)||platform.match(/mac|win|linux/)||["other"])[0]},Features:{xpath:!!(document.evaluate),air:!!(window.runtime),query:!!(document.querySelector),json:!!(window.JSON)},Plugins:{}}; -b[b.name]=true;b[b.name+parseInt(b.version,10)]=true;b.Platform[b.Platform.name]=true; -return b;})();var UA=navigator.userAgent.toLowerCase();this.isGecko=!!Browser.firefox; -this.isChrome=!!Browser.chrome;this.isSafari=!!Browser.safari;this.isSafariOld=Browser.safari&&Browser.version===2.4; -this.isWebkit=this.isSafari||this.isChrome||UA.indexOf("konqueror")!=-1;this.isOpera=!!Browser.opera; -this.isIE=!!Browser.ie;this.isWin=Browser.Platform.win;this.isMac=Browser.Platform.mac; -this.isLinux=Browser.Platform.linux;this.isIphone=Browser.Platform.ios||UA.indexOf("aspen simulator")!=-1; -this.isAIR=Browser.Features.air;this.versionWebkit=this.isWebkit?Browser.version:null; -this.versionGecko=this.isGecko?Browser.version:null;this.isGecko3=Browser.firefox3; -this.isGecko35=this.isGecko3&&Browser.version>=3.5;this.versionFF=this.isGecko?Browser.version:null; -this.versionSafari=this.isSafari?Browser.version:null;this.versionChrome=this.isChrome?Browser.version:null; -this.versionOpera=this.isOpera?Browser.version:null;this.isIE6=this.isIE&&Browser.ie6; -this.isIE7=this.isIE&&Browser.ie7;this.isIE8=this.isIE&&Browser.ie8;this.isIE7Emulate=this.isIE&&document.documentMode&&Browser.ie7; -this.isIE=this.isIE?Browser.version:null;try{}catch(e){this.isDeskrun=false;}},setCompatFlags:function(){this.TAGNAME=apf.isIE?"baseName":"localName"; -this.styleSheetRules=apf.isIE?"rules":"cssRules";this.brokenHttpAbort=apf.isIE6; -this.canUseHtmlAsXml=apf.isIE;this.supportNamespaces=!apf.isIE;this.cannotSizeIframe=apf.isIE; -this.hasConditionCompilation=apf.isIE;this.supportOverflowComponent=apf.isIE;this.hasFlexibleBox=apf.versionGecko>=3||(apf.isWebkit&&apf.versionWebkit>=3.2); -this.hasFileApi=!!(window.File&&window.FileReader&&window.Blob&&window.FileError); -this.hasEventSrcElement=apf.isIE;this.canHaveHtmlOverSelects=!apf.isIE6&&!apf.isIE5; -this.hasInnerText=apf.isIE;this.hasMsRangeObject=apf.isIE;this.descPropJs=apf.isIE; -this.hasClickFastBug=apf.isIE;this.hasExecScript=window.execScript?true:false;this.canDisableKeyCodes=apf.isIE; -this.hasTextNodeWhiteSpaceBug=apf.isIE||apf.isIE>=8;this.hasCssUpdateScrollbarBug=apf.isIE; -this.canUseInnerHtmlWithTables=!apf.isIE;this.hasSingleResizeEvent=!apf.isIE;this.hasStyleFilters=apf.isIE; -this.supportOpacity=!apf.isIE||apf.isIE>=9;this.supportPng24=!apf.isIE6&&!apf.isIE5; -this.cantParseXmlDefinition=apf.isIE50;this.hasDynamicItemList=!apf.isIE||apf.isIE>=7; -this.canImportNode=apf.isIE;this.hasSingleRszEvent=!apf.isIE;this.hasXPathHtmlSupport=!apf.isIE; -this.hasFocusBug=apf.isIE;this.hasHeightAutoDrawBug=apf.isIE&&apf.isIE<8;this.hasReadyStateBug=apf.isIE50; -this.dateSeparator=apf.isIE?"-":"/";this.canCreateStyleNode=!apf.isIE;this.supportFixedPosition=!apf.isIE||apf.isIE>=7; -this.hasHtmlIdsInJs=apf.isIE&&apf.isIE<8||apf.isWebkit;this.needsCssPx=!apf.isIE; -this.hasCSSChildOfSelector=!apf.isIE||apf.isIE>=8;this.hasStyleAnchors=!apf.isIE||apf.isIE>=8; -this.styleAttrIsObj=apf.isIE<8;this.hasAutocompleteXulBug=apf.isGecko;this.loadsLocalFilesSync=apf.isIE||apf.isGecko; -this.mouseEventBuffer=apf.isIE?20:6;this.hasComputedStyle=typeof document.defaultView!="undefined"&&typeof document.defaultView.getComputedStyle!="undefined"; -this.w3cRange=Boolean(window.getSelection);this.locale=(apf.isIE?navigator.userLanguage:navigator.language).toLowerCase(); -this.characterSet=document.characterSet||document.defaultCharset||"utf-8";var t=document.createElement("div"); -this.hasContentEditable=(typeof t.contentEditable=="string"||typeof t.contentEditable=="boolean"); -apf.hasContentEditableContainerBug=apf.isWebkit;var props=["transform","OTransform","KhtmlTransform","MozTransform","WebkitTransform"],prefixR=["","O","Khtml","Moz","Webkit"],prefixC=["","o-","khtml-","moz-","webkit-"],events=["transitionend","transitionend","transitionend","transitionend","webkitTransitionEnd"],i=0,l=5; -this.supportCSSAnim=false;this.supportCSSTransition=false;for(;i8; -this.hasHtml5XDomain=apf.versionGecko>=3.5;this.supportCanvas=!!document.createElement("canvas").getContext; -this.supportCanvasText=!!(this.supportCanvas&&typeof document.createElement("canvas").getContext("2d").fillText=="function"); -this.hasVideo=!!document.createElement("video")["canPlayType"];this.hasAudio=!!document.createElement("audio")["canPlayType"]; -this.supportHashChange=("onhashchange" in self)&&(!apf.isIE||apf.isIE>=8);if(self.XMLHttpRequest){var xhr=new XMLHttpRequest(); -this.hasXhrProgress=!!xhr.upload;if(this.hasXhrBinary=!!(xhr.sendAsBinary||xhr.upload)){this.hasHtml5File=!!(File&&File.prototype.getAsDataURL); -this.hasHtml5FileSlice=!!(File&&File.prototype.slice);}}else{this.hasXhrProgress=this.hasXhrBinary=this.hasHtml5File=this.hasHtml5FileSlice=false; -}this.windowHorBorder=this.windowVerBorder=apf.isIE8&&(!self.frameElement||parseInt(self.frameElement.frameBorder))?4:0; -t=document.createElement("input");var _self=this;(function(props){for(var i in props){t.setAttribute("type",i); -_self["hasInput"+i.charAt(0).toUpperCase()+i.substr(1).replace("-l","L")]=!!(t.type!=="text"); -}})({search:1,tel:1,url:1,email:1,datetime:1,date:1,month:1,week:1,time:1,"datetime-local":1,number:1,range:1,color:1}); -t=null;delete t;this.enableAnim=!apf.isIE||apf.isIE>8;this.animSteps=apf.isIE?0.3:1; -this.animInterval=apf.isIE?7:1;this.CSSFLOAT=apf.isIE?"styleFloat":"cssFloat";this.CSSPREFIX=apf.isGecko?"Moz":(apf.isWebkit?"webkit":""); -this.CSSPREFIX2=apf.isGecko?"-moz":(apf.isWebkit?"-webkit":"");this.INLINE=apf.isIE&&apf.isIE<8?"inline":"inline-block"; -this.needZoomForLayout=apf.isIE&&apf.isIE<8;this.maxHttpRetries=apf.isOpera?0:3; -this.percentageMatch=new RegExp();this.percentageMatch.compile("([\\-\\d\\.]+)\\%","g"); -this.reMatchXpath=new RegExp();this.reMatchXpath.compile("(^|\\|)(?!\\@|[\\w-]+::)","g"); -apf.isGears=!!apf.initGears()||0;},hasGeoLocation:function(){return typeof apf.geolocation!="undefined"&&apf.geolocation.init(); -},extend:function(dest,src){var prop,i,x=!dest.notNull;if(arguments.length==2){for(prop in src){if(x||src[prop]){dest[prop]=src[prop]; -}}return dest;}for(i=1;i".replace(/(\w+)\s*=\s*([^\>="'\s ]+)( |\s|\>|\/\>)/g,'$1="$2"$3').replace(/ disabled /g," disabled='true' ").replace(/\]\]\>/g,"]]>").replace(/<(\w+)(\s[^>]*[^\/])?>/g,function(m,tag,c){if(tags[tag]){return"<"+tag+(c||"")+"/>"; -}else{return m;}});}catch(e){return;}var xmlNode=apf.getAmlDocFromString("
"+strXml+"
").documentElement; -while(xmlNode.childNodes.length>1){xmlNode.removeChild(xmlNode.lastChild);}apf.AppNode.appendChild(xmlNode); -};}else{findAml=function(htmlNode){var strXml=htmlNode.outerHTML.replace(/ _moz-userdefined=""/g,""),xmlNode=apf.getAmlDocFromString("
"+strXml+"
").documentElement; -while(xmlNode.childNodes.length>1){xmlNode.removeChild(xmlNode.lastChild);}if(apf.isWebkit){xmlNode=apf.AppNode.ownerDocument.importNode(xmlNode,true); -}apf.AppNode.appendChild(xmlNode);};}var match=document.body.outerHTML.match(/(\w+)\s*=\s*["']http:\/\/ajax\.org\/2005\/aml["']/); -if(!match){return false;}var strXmlns="xmlns:"+match[0],prefix=(RegExp.$1||"").toUpperCase(); -if(apf.isOpera){prefix=prefix.toLowerCase();}if(!prefix){return false;}prefix+=":"; -apf.AppNode=apf.getAmlDocFromString("<"+prefix.toLowerCase()+"application "+strXmlns+" />").documentElement; -var temp,loop,cnode,isPrefix=false,id=0,node=document.body;while(node){isPrefix=node.nodeType==1&&node.tagName.substr(0,2)==prefix; -if(isPrefix){findAml(cnode=node);if(apf.isIE){loop=node;var count=1,next=loop.nextSibling; -if(next){loop.parentNode.removeChild(loop);while(next&&(next.nodeType!=1||next.tagName.indexOf(prefix)>-1)){if(next.nodeType==1){count+=next.tagName.charAt(0)=="/"?-1:1; -}if(count==0){if(temp){temp.parentNode.removeChild(temp);}temp=next;break;}next=(loop=next).nextSibling; -if(!next){next=loop;break;}if(loop.nodeType==1){loop.parentNode.removeChild(loop); -if(temp){temp.parentNode.removeChild(temp);temp=null;}}else{if(temp){temp.parentNode.removeChild(temp); -}temp=loop;}}node=next;}else{if(temp){temp.parentNode.removeChild(temp);}temp=loop; -}}else{if(temp){temp.parentNode.removeChild(temp);}temp=node;}if(apf.amlParts.length&&apf.amlParts[apf.amlParts.length-1][1]==cnode){apf.amlParts[apf.amlParts.length-1][1]=-1; -}apf.amlParts.push([node.parentNode,apf.isIE?node.nextSibling:node.nextSibling]); -}else{if(node.tagName=="SCRIPT"&&node.getAttribute("src")&&(node.getAttribute("src").indexOf("ajax.org")>-1)){var strXml=node.outerHTML.replace(/</g,"<").replace(/>/g,">").replace(/&/g,"&").replace(/]*\>\s*<\!\[CDATA\[>?/i,"").replace(/]*\>(?:<\!\-\-)?/i,"").replace(/(\/\/)?\s*\&\#8211;>\s*<\/SCRIPT>/i,"").replace(/\-\->\s*<\/SCRIPT>/i,"").replace(/\]\](?:\>\;|>)\s*<\/SCRIPT>/i,"").replace(/<\/SCRIPT>$/mi,"").replace(/<\/?\s*(?:p|br)\s*\/?>/ig,"").replace(/<\!--\s*.*?\s*-->\s*"+strXml+"").documentElement; -if(apf.isWebkit){xmlNode=apf.AppNode.ownerDocument.importNode(xmlNode,true);}apf.AppNode.appendChild(xmlNode); -apf.amlParts.push([node.parentNode,node.nextSibling]);}}}if(!isPrefix&&node.firstChild||node.nextSibling){if(!isPrefix&&node.firstChild){node=node.firstChild; -}else{node=node.nextSibling;}}else{do{node=node.parentNode;if(node.tagName=="BODY"){node=null; -}}while(node&&!node.nextSibling);if(node){node=node.nextSibling;}}}if(temp){temp.parentNode.removeChild(temp); -}},parseAppMarkup:function(docElement){var isEmptyDocument=false;if(this.parseStrategy==1||!this.parseStrategy&&!docElement&&document.documentElement.outerHTML.split(">",1)[0].indexOf(apf.ns.aml)==-1){this.parsePartialAml(docElement); -if(this.parseStrategy==1||apf.amlParts.length){apf.isParsingPartial=true;apf.loadAmlIncludes(apf.AppNode); -if(!self.ERROR_HAS_OCCURRED){apf.initialize();}return;}else{isEmptyDocument=true; -}}if(isEmptyDocument&&document.documentElement.outerHTML.split(">",1)[0].indexOf(apf.ns.aml)==-1){return false; -}if(this.parseStrategy==21||!this.parseStrategy&&!docElement){return apf.oHttp.get((apf.alternativeAml||document.body&&document.body.getAttribute("xmlurl")||location.href).split(/#/)[0],{callback:function(xmlString,state,extra){if(state!=apf.SUCCESS){var oError=new Error(apf.formatErrorString(0,null,"Loading XML application data","Could not load XML from remote source: "+extra.message)); -if(extra.tpModule.retryTimeout(extra,state,null,oError)===true){return true;}throw oError; -}var str=xmlString.replace(/\<\!DOCTYPE[^>]*>/,"").replace(/^[\r\n\s]*/,"");if(!apf.supportNamespaces){str=str.replace(/xmlns\=\"[^"]*\"/g,""); -}if(self.ERROR_HAS_OCCURRED){return;}if(apf.isIE){document.body.innerHTML="";}else{var nodes=document.body.childNodes; -for(var i=nodes.length-1;i>=0;i--){nodes[i].parentNode.removeChild(nodes[i]);}}document.documentElement.style.display="block"; -document.body.style.display="block";apf.initialize(str);},ignoreOffline:true}); -}else{document.body.style.display="block";if(!self.ERROR_HAS_OCCURRED){apf.initialize(docElement.outerHTML||docElement.xml); -}}},namespaces:{},setNamespace:function(namespaceURI,oNamespace){this.namespaces[namespaceURI]=oNamespace; -oNamespace.namespaceURI=namespaceURI;},initialize:function(xmlStr){if(apf.initialized){return; -}apf.initialized=true;apf.console.info("Initializing...");clearInterval(apf.Init.interval); -apf.Init.run();var bodyMarginTop=parseFloat(apf.getStyle(document.body,"marginTop")); -apf.doesNotIncludeMarginInBodyOffset=(document.body.offsetTop!==bodyMarginTop); -if(apf.isParsingPartial){apf.config.setDefaults();apf.hasSingleRszEvent=true;var pHtmlNode=document.body; -var lastChild=pHtmlNode.lastChild;apf.AmlParser.parseMoreAml(apf.AppNode,pHtmlNode,null,true,false); -var pNode,firstNode,next,info,lastBefore=null,loop=pHtmlNode.lastChild;while(loop&&lastChild!=loop){info=apf.amlParts[loop.getAttribute("jid")]; -next=loop.previousSibling;if(info){pNode=info[0];if("P".indexOf(pNode.tagName)>-1){lastBefore=pNode.parentNode.insertBefore(apf.getNode(loop,[0]),pNode); -}else{firstNode=apf.getNode(loop,[0]);while(firstNode){if(firstNode){lastBefore=pNode.insertBefore(firstNode,typeof info[1]=="number"?lastBefore:info[1]); -}else{lastBefore=typeof info[1]=="number"?lastBefore:info[1];}firstNode=apf.getNode(loop,[0]); -}}loop.parentNode.removeChild(loop);}loop=next;}$setTimeout("apf.layout.forceResize();"); -}else{apf.window.init(xmlStr);}},execDeferred:function(){var len=apf.load_events.length; -while(len--){(apf.load_events.shift())();}},load_events:[],load_timer:null,load_done:false,load_init:null,addDomLoadEvent:function(func){if(!this.$bdetect){this.browserDetect(); -}if(apf.load_done){return func();}if(!apf.load_init){apf.load_init=function(){if(apf.load_done){return; -}clearInterval(apf.load_timer);apf.load_timer=null;apf.load_done=true;if(apf.started){apf.execDeferred(); -}};}apf.load_events.push(func);if(func&&apf.load_events.length==1){var doc=document,UNDEF="undefined"; -if((typeof doc.readyState!=UNDEF&&doc.readyState=="complete")||(doc.getElementsByTagName("body")[0]||doc.body)){return apf.load_init(); -}if(doc.addEventListener&&!apf.isOpera){window.addEventListener("DOMContentLoaded",apf.load_init,false); -}else{if(apf.isIE&&window==top){apf.load_timer=setInterval(function(){try{doc.documentElement.doScroll("left"); -}catch(ex){$setTimeout(arguments.callee,0);return;}apf.load_init();},10);}else{if(apf.isOpera){doc.addEventListener("DOMContentLoaded",function(){apf.load_timer=setInterval(function(){for(var i=0,l=doc.styleSheets.length; -i=0;i++){if(!aSheets[i]||aSheets[i].getAttribute("rel")!="stylesheet"){aSheets.splice(i,0); -}}iSheets=aSheets.length;apf.load_timer=setInterval(function(){if(/loaded|complete/.test(doc.readyState)&&doc.styleSheets.length==iSheets){apf.load_init(); -}},10);}else{var old_onload=window.onload;window.onload=function(){apf.load_init(); -if(old_onload){old_onload();}};}}}}}},fireEvent:function(el,type,e,capture){if(el.dispatchEvent){el.dispatchEvent(type,e,capture); -}else{el.fireEvent("on"+type,e);}},addListener:function(el,type,fn,capture){if(el.addEventListener){el.addEventListener(type,fn,capture||false); -}else{if(el.attachEvent){el.attachEvent("on"+type,fn);}}return this;},removeListener:function(el,type,fn,capture){if(el.removeEventListener){el.removeEventListener(type,fn,capture||false); -}else{if(el.detachEvent){el.detachEvent("on"+type,fn);}}return this;},stopEvent:function(e){this.stopPropagation(e).preventDefault(e); -return false;},stopPropagation:function(e){if(e.stopPropagation){e.stopPropagation(); -}else{e.cancelBubble=true;}return this;},preventDefault:function(e){if(e.preventDefault){e.preventDefault(); -}else{e.returnValue=false;}return this;},unload:function(exclude){this.isDestroying=true; -this.popup.destroy();var node,i=0,l=this.all.length;for(;i-1||exclNr==3){options={xpathmode:2};}else{if(exclNr==2){options={nostring:true}; -}else{if(exclNr===0){options={parsecode:true};}}}if(this.liveedit){(options||(options={})).liveedit=true; -}if(pValue&&pValue.dataType==apf.FUNCTION){var fParsed=pValue;pValue="";}else{var fParsed=apf.lm.compile(pValue,options); -}if(prop==MODEL){(this.$modelParsed=fParsed).instruction=pValue;}if(fParsed.type==2){this[prop]=!pValue; -return this.setProperty(prop,fParsed.str,null,null,10);}var check=1;if(exclNr==2||fParsed.xpaths.length&&exclNr!=1){if(!this.hasFeature(apf.__DATABINDING__)){this.implement(apf.StandardBinding); -if(this.$attrExcludePropBind[prop]==1){check=0;}}if(check){this.$addAttrBind(prop,fParsed,pValue); -}}var matches=exclNr&&exclNr!=3&&prop!=MODEL?{}:fParsed.props,found=false,_self=this,o,node,bProp,p; -for(p in matches){if(typeof matches[p]==FUN){continue;}o=p.split(".");if(o.length>2){bProp=o.pop(); -try{node=eval(o.join("."));}catch(e){if(arguments[2]){apf.console.warn("[287] Could not execute binding test : "+pValue.replace(/-1){return;}s.unshift(callback);var f;if(f=this.$eventsStack["$event."+eventName]){f[0].call(this,callback); -}};this.removeEventListener=function(eventName,callback,useCapture){var stack=(useCapture?this.$captureStack:this.$eventsStack)[eventName]; -if(stack){if(this.$eventDepth){stack=(useCapture?this.$captureStack:this.$eventsStack)[eventName]=stack.slice(); -}stack.remove(callback);if(!stack.length){delete (useCapture?this.$captureStack:this.$eventsStack)[eventName]; -}}};this.hasEventListener=function(eventName){return(this.$eventsStack[eventName]&&this.$eventsStack[eventName].length>0); -};this.destroy=function(deep,clean){if(typeof this.$uniqueId==UNDEF&&this.nodeType!=2){return; -}this.$amlLoaded=false;this.$amlDestroyed=true;if(this.$destroy){this.$destroy(); -}this.dispatchEvent("DOMNodeRemoved",{relatedNode:this.parentNode,bubbles:!apf.isDestroying}); -this.dispatchEvent("DOMNodeRemovedFromDocument");apf.all[this.$uniqueId]=undefined; -if(!this.nodeFunc&&!this.nodeType){try{if(this.id||this.name){self[this.id||this.name]=null; -}}catch(ex){}return;}if(this.$ext&&!this.$ext.isNative){if(this.nodeType==1&&this.localName!="a"){this.$ext.oncontextmenu=this.$ext.host=null; -}if(clean){if(this.localName!="collection"&&this.$ext.parentNode){this.$ext.parentNode.removeChild(this.$ext); -}}}if(this.$int&&!this.$int.isNative&&this.$int.nodeType==1&&this.localName!="a"){this.$int.host=null; -}this.$aml=null;if(deep&&this.childNodes){var nodes=this.childNodes;for(i=nodes.length-1; -i>=0;i--){if(nodes[i].destroy){nodes[i].destroy(true,clean&&this.localName=="collection"); -}}this.childNodes=null;}if(this.parentNode&&this.removeNode){this.removeNode(); -}else{if(this.ownerElement&&!this.ownerElement.$amlDestroyed){this.ownerElement.removeAttributeNode(this); -}}if(this.$focussable&&this.focussable){apf.window.$removeFocus(this);}if(this.attributes){var attr=this.attributes; -for(var i=attr.length-1;i>=0;i--){this.$clearDynamicProperty(attr[i].nodeName); -attr[i].destroy();}}try{if(this.id||this.name){self[this.id||this.name]=null;}}catch(ex){}apf.nameserver.remove(this.localName,this); -};})();apf.extend(apf,new apf.Class().$init());apf.Init.run("class");apf.color={colorshex:{aliceblue:15792383,antiquewhite:16444375,aqua:65535,aquamarine:8388564,azure:15794175,beige:16119260,bisque:16770244,black:0,blanchedalmond:16772045,blue:255,blueviolet:9055202,brown:10824234,burlywood:14596231,cadetblue:6266528,chartreuse:8388352,chocolate:13789470,coral:16744272,cornflowerblue:6591981,cornsilk:16775388,crimson:14423100,cyan:65535,darkblue:139,darkcyan:35723,darkgoldenrod:12092939,darkgray:11119017,darkgrey:11119017,darkgreen:25600,darkkhaki:12433259,darkmagenta:9109643,darkolivegreen:5597999,darkorange:16747520,darkorchid:10040012,darkred:9109504,darksalmon:15308410,darkseagreen:9419919,darkslateblue:4734347,darkslategray:3100495,darkslategrey:3100495,darkturquoise:52945,darkviolet:9699539,deeppink:16716947,deepskyblue:49151,dimgray:6908265,dimgrey:6908265,dodgerblue:2003199,firebrick:11674146,floralwhite:16775920,forestgreen:2263842,fuchsia:16711935,gainsboro:14474460,ghostwhite:16316671,gold:16766720,goldenrod:14329120,gray:8421504,grey:8421504,green:32768,greenyellow:11403055,honeydew:15794160,hotpink:16738740,indianred:13458524,indigo:4915330,ivory:16777200,khaki:15787660,lavender:15132410,lavenderblush:16773365,lawngreen:8190976,lemonchiffon:16775885,lightblue:11393254,lightcoral:15761536,lightcyan:14745599,lightgoldenrodyellow:16448210,lightgray:13882323,lightgrey:13882323,lightgreen:9498256,lightpink:16758465,lightsalmon:16752762,lightseagreen:2142890,lightskyblue:8900346,lightslategray:7833753,lightslategrey:7833753,lightsteelblue:11584734,lightyellow:16777184,lime:65280,limegreen:3329330,linen:16445670,magenta:16711935,maroon:8388608,mediumaquamarine:6737322,mediumblue:205,mediumorchid:12211667,mediumpurple:9662680,mediumseagreen:3978097,mediumslateblue:8087790,mediumspringgreen:64154,mediumturquoise:4772300,mediumvioletred:13047173,midnightblue:1644912,mintcream:16121850,mistyrose:16770273,moccasin:16770229,navajowhite:16768685,navy:128,oldlace:16643558,olive:8421376,olivedrab:7048739,orange:16753920,orangered:16729344,orchid:14315734,palegoldenrod:15657130,palegreen:10025880,paleturquoise:11529966,palevioletred:14184595,papayawhip:16773077,peachpuff:16767673,peru:13468991,pink:16761035,plum:14524637,powderblue:11591910,purple:8388736,red:16711680,rosybrown:12357519,royalblue:4286945,saddlebrown:9127187,salmon:16416882,sandybrown:16032864,seagreen:3050327,seashell:16774638,sienna:10506797,silver:12632256,skyblue:8900331,slateblue:6970061,slategray:7372944,slategrey:7372944,snow:16775930,springgreen:65407,steelblue:4620980,tan:13808780,teal:32896,thistle:14204888,tomato:16737095,turquoise:4251856,violet:15631086,wheat:16113331,white:16777215,whitesmoke:16119285,yellow:16776960,yellowgreen:10145074},fixHSB:function(hsb){return{h:Math.min(360,Math.max(0,hsb.h)),s:Math.min(100,Math.max(0,hsb.s)),b:Math.min(100,Math.max(0,hsb.b))}; -},fixRGB:function(rgb){return{r:Math.min(255,Math.max(0,rgb.r)),g:Math.min(255,Math.max(0,rgb.g)),b:Math.min(255,Math.max(0,rgb.b))}; -},fixHex:function(hex){var len=6-hex.length;if(len>0){var ch=hex.charAt(hex.length-1); -var o=[],i=0;o.push(hex);for(;i-1)?hex.substring(1):hex),16); -return{r:hex>>16,g:(hex&65280)>>8,b:(hex&255)};},hexToHSB:function(hex){return this.RGBToHSB(this.hexToRGB(hex)); -},RGBToHSB:function(rgb){var hsb={h:0,s:0,b:0};var min=Math.min(rgb.r,rgb.g,rgb.b),max=Math.max(rgb.r,rgb.g,rgb.b),delta=max-min; -hsb.b=max;if(max!=0){}hsb.s=max!=0?255*delta/max:0;if(hsb.s!=0){if(rgb.r==max){hsb.h=(rgb.g-rgb.b)/delta; -}else{if(rgb.g==max){hsb.h=2+(rgb.b-rgb.r)/delta;}else{hsb.h=4+(rgb.r-rgb.g)/delta; -}}}else{hsb.h=-1;}hsb.h*=60;if(hsb.h<0){hsb.h+=360;}hsb.s*=100/255;hsb.b*=100/255; -return hsb;},HSBToRGB:function(hsb){var rgb={},h=Math.round(hsb.h),s=Math.round(hsb.s*255/100),v=Math.round(hsb.b*255/100); -if(s==0){rgb.r=rgb.g=rgb.b=v;}else{var t1=v,t2=(255-s)*v/255,t3=(t1-t2)*(h%60)/60; -if(h==360){h=0;}if(h<60){rgb.r=t1,rgb.b=t2,rgb.g=t2+t3;}else{if(h<120){rgb.g=t1,rgb.b=t2,rgb.r=t1-t3; -}else{if(h<180){rgb.g=t1,rgb.r=t2,rgb.b=t2+t3;}else{if(h<240){rgb.b=t1,rgb.r=t2,rgb.g=t1-t3; -}else{if(h<300){rgb.b=t1,rgb.g=t2,rgb.r=t2+t3;}else{if(h<360){rgb.r=t1,rgb.g=t2,rgb.b=t1-t3; -}else{rgb.r=0,rgb.g=0,rgb.b=0;}}}}}}}return{r:Math.round(rgb.r),g:Math.round(rgb.g),b:Math.round(rgb.b)}; -},RGBToHex:function(rgb){return("00000"+(rgb.r<<16|rgb.g<<8|rgb.b).toString(16)).slice(-6); -},HSBToHex:function(hsb){return this.RGBToHex(this.HSBToRGB(hsb));}};apf.AbstractEvent=function(event,win){win=win||window; -var doc=win.document;event=event||win.event;if(event.$extended){return event;}this.$extended=true; -this.event=event;this.type=event.type;this.target=event.target||event.srcElement; -while(this.target&&this.target.nodeType==3){this.target=this.target.parentNode; -}if(this.type.indexOf("key")!=-1){this.code=event.which||event.keyCode;}else{if(this.type.match(/(click|mouse|menu)/i)){doc=(!doc.compatMode||doc.compatMode=="CSS1Compat")?doc.html:doc.body; -this.page={x:event.pageX||event.clientX+(doc?doc.scrollLeft:0),y:event.pageY||event.clientY+(doc?doc.scrollTop:0)}; -this.client={x:(event.pageX)?event.pageX-win.pageXOffset:event.clientX,y:(event.pageY)?event.pageY-win.pageYOffset:event.clientY}; -if(this.type.match(/DOMMouseScroll|mousewheel/)){this.wheel=(event.wheelDelta)?event.wheelDelta/120:-(event.detail||0)/3; -}this.rightClick=(event.which==3)||(event.button==2);this.relatedTarget=null;if(this.type.match(/over|out/)){if(this.type=="mouseover"){this.relatedTarget=event.relatedTarget||event.fromElement; -}else{if(this.type=="mouseout"){this.relatedTarget=event.relatedTarget||event.toElement; -}else{try{while(this.relatedTarget&&this.relatedTarget.nodeType==3){this.relatedTarget=this.relatedTarget.parentNode; -}}catch(e){}}}}}}this.shift=Boolean(event.shiftKey);this.control=Boolean(event.ctrlKey); -this.alt=Boolean(event.altKey);this.meta=Boolean(event.metaKey);this.stop=function(){return this.stopPropagation().preventDefault(); -};this.stopPropagation=function(){if(this.event.stopPropagation){this.event.stopPropagation(); -}else{this.event.cancelBubble=true;}return this;};this.preventDefault=function(){if(this.event.preventDefault){this.event.preventDefault(); -}else{this.event.returnValue=false;}return this;};};apf.AbstractEvent.KEYS={enter:13,up:38,down:40,left:37,right:39,esc:27,space:32,backspace:8,tab:9,"delete":46,fromCode:function(code){for(var i in this){if(this[i]==code){return i; -}return null;}}};apf.AbstractEvent.stop=function(event){return(new apf.AbstractEvent(event)).stop(); -};apf.asyncForEach=function(list,async,callback){var i=0;var len=list.length;if(!len){return callback(null,[]); -}async(list[i],function handler(err){if(err){return callback(err);}i++;if(i=len)?len-1:(from<0)?from+len:len-1;i>=0;i--){if(this[i]===obj){return i; -}}return -1;};Array.prototype.pushUnique=function(){var item,i=0,l=arguments.length; -for(;i=0;i--){if(this[i]!=obj){continue;}this.splice(i,1);}return this;};Array.prototype.removeIndex=function(i){if(!this.length){return null; -}return this.splice(i,1);};Array.prototype.insertIndex=function(obj,i){this.splice(i,0,obj); -};Array.prototype.invert=Array.prototype.reverse=Array.prototype.reverse||function(){var l=this.length-1; -for(var temp,i=0;i]+>/gi,""); -};String.prototype.escape=function(){return escape(this);};String.prototype.toXml=function(){var node=apf.getXml(""+this+""); -if(node.childNodes.length==1){return node.childNodes[0];}else{var docFrag=node.ownerDocument.createDocumentFragment(),nodes=node.childNodes; -while(nodes.length){docFrag.appendChild(nodes[0]);}return docFrag;}};if(typeof window!="undefined"&&typeof window.document!="undefined"&&typeof window.document.createElement=="function"){String.prototype.escapeHTML=function(){this.escapeHTML.text.data=this; -return this.escapeHTML.div.innerHTML;};String.prototype.unescapeHTML=function(){var div=document.createElement("div"); -div.innerHTML=this.stripTags();if(div.childNodes[0]){if(div.childNodes.length>1){var out=[]; -for(var i=0;i".escapeHTML()!=="<\n>"){String.prototype.escapeHTML=null;}if("<\n>".unescapeHTML()!=="<\n>"){String.prototype.unescapeHTML=null; -}}if(!String.prototype.escapeHTML){String.prototype.escapeHTML=function(){return this.replace(/&/g,"&").replace(//g,">"); -};}if(!String.prototype.unescapeHTML){String.prototype.unescapeHTML=function(){return this.stripTags().replace(/</g,"<").replace(/>/g,">").replace(/&/g,"&"); -};}String.prototype.truncate=function(nr,ellipsis){return this.length>=nr?this.substring(0,nr-(ellipsis?4:1))+(ellipsis?"...":""):this; -};String.prototype.pad=function(len,pad,dir){return dir?(this+Array(len).join(pad)).slice(0,len):(Array(len).join(pad)+this).slice(-len); -};apf.PAD_LEFT=false;apf.PAD_RIGHT=true;String.prototype.splitSafe=function(separator,limit,bLowerCase){return(bLowerCase&&this.toLowerCase()||this).replace(/(?:^\s+|\n|\s+$)/g,"").split(new RegExp("[\\s ]*"+separator+"[\\s ]*","g"),limit||999); -};String.prototype.appendRandomNumber=function(length){for(var arr=[],i=1;i<=length; -i++){arr.push(apf.randomGenerator.generate(1,9));}return this.toString()+arr.join(""); -};String.prototype.prependRandomNumber=function(length){for(var arr=[],i=1;i<=length; -i++){arr.push(apf.randomGenerator.generate(1,9));}return arr.join("")+this.toString(); -};String.prototype.sprintf=function(){var str=this.toString(),i=0,inx=str.indexOf("%s"); -while(inx>=0){var replacement=arguments[i++]||" ";str=str.substr(0,inx)+replacement+str.substr(inx+2); -inx=str.indexOf("%s");}return str;};if(!Date.now){Date.now=function now(){return +new Date(); -};}apf.flash=(function(){function getControlVersion(){var version,axo;try{axo=new ActiveXObject("ShockwaveFlash.ShockwaveFlash.7"); -version=axo.GetVariable("$version");}catch(e){}if(!version){try{axo=new ActiveXObject("ShockwaveFlash.ShockwaveFlash.6"); -version="WIN 6,0,21,0";axo.AllowScriptAccess="always";version=axo.GetVariable("$version"); -}catch(e){}}if(!version){try{axo=new ActiveXObject("ShockwaveFlash.ShockwaveFlash.3"); -version=axo.GetVariable("$version");}catch(e){}}if(!version){try{axo=new ActiveXObject("ShockwaveFlash.ShockwaveFlash.3"); -version="WIN 3,0,18,0";}catch(e){}}if(!version){try{axo=new ActiveXObject("ShockwaveFlash.ShockwaveFlash"); -version="WIN 2,0,0,11";}catch(e){version=-1;}}return version;}function getSwfVersion(){var flashVer=-1,sAgent=navigator.userAgent.toLowerCase(); -if(navigator.plugins!=null&&navigator.plugins.length>0){if(navigator.plugins["Shockwave Flash 2.0"]||navigator.plugins["Shockwave Flash"]){var swVer2=navigator.plugins["Shockwave Flash 2.0"]?" 2.0":"",swfDescr=navigator.plugins["Shockwave Flash"+swVer2].description,aDescr=swfDescr.split(" "),aTempMaj=aDescr[2].split("."),nMajor=aTempMaj[0],nMinor=aTempMaj[1],sRev=aDescr[3]; -if(sRev==""){sRev=aDescr[4];}if(sRev[0]=="d"){sRev=sRev.substring(1);}else{if(sRev[0]=="r"){sRev=sRev.substring(1); -if(sRev.indexOf("d")>0){sRev=sRev.substring(0,sRev.indexOf("d"));}}}flashVer=nMajor+"."+nMinor+"."+sRev; -}}else{if(sAgent.indexOf("webtv/2.6")!=-1){flashVer=4;}else{if(sAgent.indexOf("webtv/2.5")!=-1){flashVer=3; -}else{if(sAgent.indexOf("webtv")!=-1){flashVer=2;}else{if(apf.isIE&&!apf.isOpera){flashVer=getControlVersion(); -}}}}}return flashVer;}function detectFlashVersion(reqMajorVer,reqMinorVer,reqRevision){var versionStr=getSwfVersion(); -if(versionStr==-1){return false;}if(versionStr!=0){var aVersions;if(apf.isIE&&!apf.isOpera){var aTemp=versionStr.split(" "),sTemp=aTemp[1]; -aVersions=sTemp.split(",");}else{aVersions=versionStr.split(".");}var nMajor=aVersions[0],nMinor=aVersions[1],sRev=aVersions[2]; -if(nMajor>parseFloat(reqMajorVer)){return true;}if(nMajor==parseFloat(reqMajorVer)){if(nMinor>parseFloat(reqMinorVer)){return true; -}if(nMinor==parseFloat(reqMinorVer)&&sRev>=parseFloat(reqRevision)){return true; -}}return false;}}function generateObj(objAttrs,params,embedAttrs,stdout){if(stdout=="undefined"){stdout=false; -}var i,str=[];if(apf.isIE&&!apf.isOpera){str.push("");for(i in params){str.push(''); -}str.push("");}else{str.push("");}var sOut=str.join("");if(stdout===true){document.write(sOut); -}return sOut;}function AC_FL_RunContent(options){var ret=AC_GetArgs(options,"movie","clsid:d27cdb6e-ae6d-11cf-96b8-444553540000","application/x-shockwave-flash"); -return generateObj(ret.objAttrs,ret.params,ret.embedAttrs);}function buildContent(options){var v=isEightAvailable(); -if(isAvailable()&&!v){return buildInstaller(options||{});}if(v){return AC_FL_RunContent(options); -}return'This content requires the Adobe Flash Player.'; -}function embed(options){var obj=options.context,node=options.htmlNode,prop=options.property||"$player"; -delete options.context,delete options.htmlNode,delete options.property;var content=buildContent(options),cb=function(){$setTimeout(function(){node.innerHTML=content; -obj[prop]=getElement(options.id);$setTimeout(function(){var fail=null;if(!obj[prop]){fail="The Flash movie failed to load. Please check if you're loading the movie on a website running through http://."; -}else{if(!obj[prop].parentNode){fail="The movie has to be enabled manually because of Flashblock. No browser refresh is required."; -}else{if(obj[prop].style.display=="none"){fail="Adblock Plus blocks or hides the movie. Please enable it and refresh your browser."; -}else{if(!obj[prop].offsetWidth){fail="The Flash movie failed to load. Please check if the file exists and the path is correct."; -}}}}if(fail){if(options.onError){options.onError({message:fail});}else{obj.dispatchEvent("error",{message:fail}); -}}},1000);},200);};return apf.loaded?cb():apf.addEventListener("load",cb);}function buildInstaller(options){if(!options){options={}; -}var ret=AC_GetArgs(options,"movie","clsid:d27cdb6e-ae6d-11cf-96b8-444553540000","application/x-shockwave-flash"),MMPlayerType=(apf.isIE==true)?"ActiveX":"PlugIn",MMredirectURL=window.location; -document.title=document.title.slice(0,47)+" - Flash Player Installation";var MMdoctitle=document.title; -return AC_FL_RunContent({src:"playerProductInstall",FlashVars:"MMredirectURL="+MMredirectURL+"&MMplayerType="+MMPlayerType+"&MMdoctitle="+MMdoctitle+"",width:"100%",height:"100%",align:"middle",id:ret.embedAttrs.name,quality:"high",bgcolor:"#000000",name:ret.embedAttrs.name,allowScriptAccess:"always",type:"application/x-shockwave-flash",pluginspage:"http://www.adobe.com/go/getflashplayer"}); -}var sSrc="src|movie",sObj="onafterupdate|onbeforeupdate|onblur|oncellchange|onclick|ondblclick|ondrag|ondragend|ondragenter|ondragleave|ondragover|ondrop|onfinish|onfocus|onhelp|onmousedown|onmouseup|onmouseover|onmousemove|onmouseout|onkeypress|onkeydown|onkeyup|onload|onlosecapture|onpropertychange|onreadystatechange|onrowsdelete|onrowenter|onrowexit|onrowsinserted|onstart|onscroll|onbeforeeditfocus|onactivate|onbeforedeactivate|ondeactivate|type|codebase|id",sEmb="width|height|align|vspace|hspace|class|title|accesskey|name|tabindex"; -function AC_GetArgs(options,srcParamName,classid,mimeType){var i,name,ret={embedAttrs:{},params:{},objAttrs:{}}; -for(i in options){name=i.toLowerCase();if(name=="classid"){continue;}if(name=="pluginspage"){ret.embedAttrs[i]=options[i]; -}else{if(sSrc.indexOf(name)>-1){ret.embedAttrs.src=options[i];ret.params[srcParamName]=options[i]; -}else{if(sObj.indexOf(name)>-1){ret.objAttrs[i]=options[i];}else{if(sEmb.indexOf(name)>-1){ret.embedAttrs[i]=ret.objAttrs[i]=options[i]; -}else{ret.embedAttrs[i]=ret.params[i]=options[i];}}}}}ret.objAttrs.classid=classid; -if(mimeType){ret.embedAttrs.type=mimeType;}return ret;}function getElement(id){var elem; -if(typeof id=="object"){return id;}if(apf.isIE){return self[id];}else{elem=document[id]?document[id]:document.getElementById(id); -if(!elem){elem=apf.lookup(id);}return elem;}}var hash={},uniqueID=1;function addPlayer(player){hash[++uniqueID]=player; -return uniqueID;}function getPlayer(id){return hash[id];}function callMethod(id,methodName){var player=hash[id]; -if(player==null){throw new Error(apf.formatErrorString(0,this,"Player with id: "+id+" not found")); -}if(player[methodName]==null){throw new Error(apf.formatErrorString(0,this,"Method "+methodName+" Not found")); -}var args=[],i=2,l=arguments.length;for(;i'+__flash__argumentsToXML(arguments,2)+""); -return eval(rs);}function encode(data){if(!data||typeof data!="string"){return data; -}return data.replace(/\&([^;]*)\;/g,"&$1;").replace(//g,">").replace("\\","&custom_backslash;").replace(/\0/g,"\\0").replace(/\"/g,"""); -}function decode(data){if(data&&data.length&&typeof data!="string"){data=data[0]; -}if(!data||typeof data!="string"){return data;}return data.replace(/\&custom_lt\;/g,"<").replace(/\&custom_gt\;/g,">").replace(/\&custom_backslash\;/g,"\\").replace(/\\0/g,"\0"); -}var aIsAvailable={};function isAvailable(sVersion){if(typeof sVersion!="string"){sVersion="6.0.65"; -}var aVersion=sVersion.split(".");while(aVersion.length<3){aVersion.push("0");}if(typeof aIsAvailable[sVersion]=="undefined"){aIsAvailable[sVersion]=detectFlashVersion(parseInt(aVersion[0]),parseInt(aVersion[1]),parseInt(aVersion[2])); -}return aIsAvailable[sVersion];}function isEightAvailable(){return isAvailable("8.0.0"); -}var oSandboxTypes={remote:"remote (domain-based) rules",localwithfile:"local with file access (no internet access)",localwithnetwork:"local with network (internet access only, no local access)",localtrusted:"local, trusted (local + internet access)"}; -function getSandbox(sType){var oSandbox={type:null,description:null,noRemote:false,noLocal:false,error:null}; -oSandbox.type=sType.toLowerCase();oSandbox.description=oSandboxTypes[(typeof oSandboxTypes[oSandbox.type]!="undefined"?oSandbox.type:"unknown")]; -if(oSandbox.type=="localwithfile"){oSandbox.noRemote=true;oSandbox.noLocal=false; -oSandbox.error="Flash security note: Network/internet URLs will not load due to security restrictions. Access can be configured via Flash Player Global Security Settings Page: http://www.macromedia.com/support/documentation/en/flashplayer/help/settings_manager04.html"; -}else{if(oSandbox.type=="localwithnetwork"){oSandbox.noRemote=false;oSandbox.noLocal=true; -}else{if(oSandbox.type=="localtrusted"){oSandbox.noRemote=false;oSandbox.noLocal=false; -}}}return oSandbox;}return{isAvailable:isAvailable,isEightAvailable:isEightAvailable,buildContent:buildContent,embed:embed,encode:encode,decode:decode,getElement:getElement,addPlayer:addPlayer,getPlayer:getPlayer,callMethod:callMethod,getSandbox:getSandbox,remote:remote}; -})();apf.hotkeys={};(function(){var keyMods={ctrl:1,alt:2,option:2,shift:4,meta:8,command:8}; -this.keyNames={"8":"Backspace","9":"Tab","13":"Enter","27":"Esc","32":"Space","33":"PageUp","34":"PageDown","35":"End","36":"Home","37":"Left","38":"Up","39":"Right","40":"Down","45":"Insert","46":"Del","107":"+","112":"F1","113":"F2","114":"F3","115":"F4","116":"F5","117":"F6","118":"F7","119":"F8","120":"F9","121":"F10","122":"F11","123":"F12","188":",","219":"[","221":"]"}; -var macUnicode={meta:"\u2318",command:"\u2318",alt:"\u2325",option:"\u2325",shift:"\u21E7",esc:"\u238B",control:"\u2303",backspace:"\u232B",del:"\u2326",enter:"\u21A9"}; -var macUnicodeHtml={meta:"⌘",command:"⌘",alt:"⌥",option:"⌥",shift:"⇧",esc:"⎋",control:"ࣿ",backspace:"èB;",del:"ख",enter:"A9;"}; -this.$keys={};var _self=this,trace=0;function register(hotkey,handler,remove){var key,hashId=0,keys=hotkey.splitSafe("\\-",null,true),i=0,l=keys.length; -for(;i46&&code!=91?String.fromCharCode(code):null); -if(!hashId&&(!key||!key.match(/^F\d{1,2}$/))||!key){return;}if(_self.$keys[hashId]&&(handler=_self.$keys[hashId][key.toLowerCase()])){handler(eInfo.htmlEvent); -eInfo.returnValue=false;apf.queue.empty();}return eInfo.returnValue;};apf.removeHotkey=this.remove=this.unregister=function(hotkey,handler){var parts=hotkey.split("|"),i=0,l=parts.length; -for(;i46&&!_self.keyNames[e.keyCode]){keys.push(String.fromCharCode(e.keyCode)); -}apf.setProperty("hotkey",keys.join("-"));}});}).call(apf.hotkeys);apf.iepngfix=(function(){var sNodes=null,aNodes=null,applyPositioning=true,shim,fnLoadPngs=function(){if(!shim){shim=apf.skins.skins["default"].mediaPath+"/blank.gif"; -}if(aNodes===null){if(sNodes){aNodes=sNodes.splitSafe(",");}else{aNodes=[document]; -}}function fixMe(obj){if(obj.currentStyle.backgroundImage.match(/\.png/i)!==null){bg_fnFixPng(obj); -}if(obj.tagName=="IMG"&&obj.src.match(/\.png$/i)!==null){el_fnFixPng(obj);}if(applyPositioning&&(obj.tagName=="A"||obj.tagName=="INPUT")&&obj.style.position===""){obj.style.position="relative"; -}}for(var j=0,l=aNodes.length,node;j/g,">"); -}if(!notag){if(name!=(m=name.replace(/[^a-zA-Z0-9_-]/g,"_"))){apf.console.warn("Json2XML, invalid characters found in JSON tagname '"+name,"json2Xml"); -}name=m;}if(apf.isArray(v)){for(i=0,n=v.length;i");}if(t=(objAttr||apf.json2xml_Obj[name])){if(t==1){t={child:name.replace(/(.*)s$/,"$1")||name,key:"name",value:"value"}; -}for(i in v){if(i.charAt(0)!="@"&&!apf.json2xml_Attr[i]){if(typeof(m=v[i])=="object"){if(apf.json2xml_Obj[i]){jsonToXml(m,i,xml); -}else{xml.push("<",t.child," ",t.key,'="',cleanString(i.toString()),'" >');jsonToXml(m,i,xml,true); -xml.push("\n");}}else{xml.push("<",t.child," ",t.key,'="',i,'" '); -if(t.value){if(t.value==1){xml.push("/>");}else{xml.push(t.value,'="',cleanString(v[i].toString()),'"/>'); -}}else{xml.push(">",cleanString(v[i].toString()),"");}}}}if(!notag){xml.push("\n"); -}}else{for(i in v){if(!apf.json2xml_Attr[i]&&i.charAt(0)!="@"){if(i.match(/[^a-zA-Z0-9_-]/g)){apf.console.warn("Json2XML, invalid characters found in JSON tagname: '"+i,"json2Xml"); -}else{jsonToXml(v[i],i,xml,false);}}}if(!notag){xml.push("");}}}else{if(!notag){xml.push("/>"); -}}}else{if(!notag){xml.push("<",name,">",cleanString(v.toString()),""); -}else{xml.push(cleanString(v.toString()));}}}};return function(strJson,noError,preserveWhiteSpace){var o=(typeof strJson=="string"&&apf.isJson(strJson))?JSON.parse(strJson.replace(/""/g,'" "')):strJson,xml=[],i; -jsonToXml(o,"jsondoc",xml,false);return apf.getXmlDom(xml.join("").replace(/\t|\n/g,""),noError,true); -};})();apf.xml2json=function(xml,noattrs){var filled,out={},o,nodes=xml.childNodes,cn,i,j,n,m,u,v,w,s,t,cn1,u1,v1,t1,name; -if(!noattrs){if(m=(xml.attributes)){for(u=0,v=m.length;u-1){o.content.style.display=""; -}var x=options.x;var y=options.y;var refNode=options.ref;while(refNode&&refNode.nodeType==1){if(fixed=apf.getStyle(refNode,"position")=="fixed"){break; -}refNode=refNode.parentNode||refNode.$parentNode;}if(!fixed){if(refNode){var pos=apf.getAbsolutePosition(options.ref,o.content.offsetParent||o.content.parentNode); -x=(x||0)+pos[0];y=(y||0)+pos[1];}if(options.width||o.width){popup.style.width=((options.width||o.width)-3)+"px"; -}popup.style.position="absolute";var pOverflow=apf.getOverflowParent(o.content); -var edgeY=(pOverflow==document.documentElement?(apf.isIE?pOverflow.offsetHeight:(window.innerHeight+window.pageYOffset))+pOverflow.scrollTop:pOverflow.offsetHeight+pOverflow.scrollTop); -moveUp=options.autoCorrect&&(y+(options.height||o.height||o.content.offsetHeight))>edgeY; -if(moveUp){var value;if(refNode){value=(pos[1]-(options.height||o.height||o.content.offsetHeight)); -}else{value=(edgeY-(options.height||o.height||o.content.offsetHeight));}popup.style.top=value<0?y:value+"px"; -}else{popup.style.top=y+"px";}if(!options.noleft){var edgeX=(pOverflow==document.documentElement?(apf.isIE?pOverflow.offsetWidth:(window.innerWidth+window.pageXOffset))+pOverflow.scrollLeft:pOverflow.offsetWidth+pOverflow.scrollLeft); -moveLeft=options.autoCorrect&&(x+(options.width||o.width||o.content.offsetWidth))>edgeX; -if(moveLeft){var value=(edgeX-(options.width||o.width||o.content.offsetWidth)); -popup.style.left=value<0?x:value+"px";}else{popup.style.left=x+"px";}}}else{pos=apf.getAbsolutePosition(options.ref,refNode); -y=(y||0)+pos[1]+refNode.offsetTop;pos[0]+=refNode.offsetLeft;popup.style.position="fixed"; -popup.style.top=y+"px";if(!options.noleft){popup.style.left=x+"px";}}apf.setStyleClass(popup,moveUp?"upward":"downward",[moveUp?"downward":"upward"]); -if(options.animate){if(options.animate=="fade"){apf.tween.single(popup,{type:"fade",from:0,to:1,anim:apf.tween.NORMAL,steps:options.steps||15*apf.animSteps}); -}else{var iVal,steps=apf.isIE8?5:7,i=0;iVal=setInterval(function(){var value=++i*((options.height||o.height)/steps); -popup.style.height=value+"px";if(moveUp){popup.style.top=(y-value-(options.y||0))+"px"; -}else{(options.container||popup).scrollTop=-1*(i-steps)*((options.height||o.height)/steps); -}popup.style.display="block";if(i>=steps){clearInterval(iVal);if(options.callback){options.callback(popup); -}}},10);}}else{if(!refNode){if(options.height||o.height){popup.style.height=(options.height||o.height)+"px"; -}popup.style.top=y+"px";}popup.style.display="block";if(options.callback){options.callback(popup); -}}$setTimeout(function(){apf.popup.last=cacheId;});if(options.draggable){options.id=cacheId; -this.makeDraggable(options);}},hide:function(){if(this.isDragging){return;}var o=this.cache[this.last]; -if(o){if(o.content){o.content.style.display="none";}if(o.options&&o.options.onclose){o.options.onclose(apf.extend(o.options,{htmlNode:o.content})); -o.options.onclose=false;}}},isShowing:function(cacheId){return this.last&&this.last==cacheId&&this.cache[this.last]&&this.cache[this.last].content.style.display!="none"; -},isDragging:false,makeDraggable:function(options){if(!apf.Interactive||this.cache[options.id].draggable){return; -}var oHtml=this.cache[options.id].content;this.cache[options.id].draggable=true; -var o={$propHandlers:{},minwidth:10,minheight:10,maxwidth:10000,maxheight:10000,dragOutline:false,resizeOutline:false,draggable:true,resizable:options.resizable,$ext:oHtml,oDrag:oHtml.firstChild}; -oHtml.onmousedown=oHtml.firstChild.onmousedown=function(e){if(!e){e=event;}if(apf.hasFocusBug&&!apf.popup.focusFix[(e.srcElement||e.target).tagName]){apf.window.$focusfix(); -}(e||event).cancelBubble=true;};apf.implement.call(o,apf.Interactive);o.$propHandlers.draggable.call(o,true); -o.$propHandlers.resizable.call(o,true);},getCurrentElement:function(){return typeof this.last=="number"&&apf.lookup(this.last); -},forceHide:function(){if(this.last&&!apf.plane.current&&this.isShowing(this.last)&&this.cache[this.last]&&this.cache[this.last].options&&this.cache[this.last].options.autohide!==false){var o=apf.lookup(this.last); -if(!o){this.last=null;}else{if(o.dispatchEvent("popuphide")!==false){this.hide(); -}}}},destroy:function(){for(var cacheId in this.cache){if(this.cache[cacheId]){this.cache[cacheId].content.onmousedown=null; -apf.destroyHtmlNode(this.cache[cacheId].content);this.cache[cacheId].content=null; -this.cache[cacheId]=null;}}if(!this.popup){return;}}};apf.silverlight=(function(){var silverlightCount=0; -var __onSilverlightInstalledCalled=false;var fwlinkRoot="http://go2.microsoft.com/fwlink/?LinkID="; -var __installationEventFired=false;var onGetSilverlight=null;function onSilverlightInstalled(){window.location.reload(false); -}function isInstalled(version){if(version==undefined){version=null;}var isVersionSupported=false; -var container=null;try{var control=null;var tryNS=false;if(window.ActiveXObject){try{control=new ActiveXObject("AgControl.AgControl"); -if(version===null){isVersionSupported=true;}else{if(control.IsVersionSupported(version)){isVersionSupported=true; -}}control=null;}catch(e){tryNS=true;}}else{tryNS=true;}if(tryNS){var plugin=navigator.plugins["Silverlight Plug-In"]; -if(plugin){if(version===null){isVersionSupported=true;}else{var actualVer=plugin.description; -if(actualVer==="1.0.30226.2"){actualVer="2.0.30226.2";}var actualVerArray=actualVer.split("."); -while(actualVerArray.length>3){actualVerArray.pop();}while(actualVerArray.length<4){actualVerArray.push(0); -}var reqVerArray=version.split(".");while(reqVerArray.length>4){reqVerArray.pop(); -}var requiredVersionPart;var actualVersionPart;var index=0;do{requiredVersionPart=parseInt(reqVerArray[index]); -actualVersionPart=parseInt(actualVerArray[index]);index++;}while(index");delete slProperties.id;delete slProperties.width;delete slProperties.height; -for(var name in slProperties){if(slProperties[name]){htmlBuilder.push(''); -}}htmlBuilder.push("");return htmlBuilder.join("");}function createObjectEx(params){var parameters=params; -var html=createObject(parameters.source,parameters.parentElement,parameters.id,parameters.properties,parameters.events,parameters.initParams,parameters.context); -if(parameters.parentElement==null){return html;}}function buildPromptHTML(slPluginHelper){var slPluginHTML=""; -var urlRoot=fwlinkRoot;var version=slPluginHelper.version;if(slPluginHelper.alt){slPluginHTML=slPluginHelper.alt; -}else{if(!version){version="";}slPluginHTML="Get Microsoft Silverlight"; -slPluginHTML=slPluginHTML.replace("{1}",version);slPluginHTML=slPluginHTML.replace("{2}",urlRoot+"108181"); -}return slPluginHTML;}function getSilverlight(version){if(onGetSilverlight){onGetSilverlight(); -}var shortVer="";var reqVerArray=String(version).split(".");if(reqVerArray.length>1){var majorNum=parseInt(reqVerArray[0]); -if(isNaN(majorNum)||majorNum<2){shortVer="1.0";}else{shortVer=reqVerArray[0]+"."+reqVerArray[1]; -}}var verArg="";if(shortVer.match(/^\d+\056\d+$/)){verArg="&v="+shortVer;}followFWLink("114576"+verArg); -}function followFWLink(linkid){top.location=fwlinkRoot+String(linkid);}function HtmlAttributeEncode(strInput){var c; -var retVal="";if(strInput==null){return null;}for(var cnt=0;cnt96)&&(c<123))||((c>64)&&(c<91))||((c>43)&&(c<58)&&(c!=47))||(c==95)){retVal=retVal+String.fromCharCode(c); -}else{retVal=retVal+"&#"+c+";";}}return retVal;}function default_error_handler(sender,args){var iErrorCode; -var errorType=args.ErrorType;iErrorCode=args.ErrorCode;var errMsg=["\nSilverlight error message \n ErrorCode: ",iErrorCode,"\n ErrorType: ",errorType," \n Message: ",args.ErrorMessage," \n"]; -if(errorType=="ParserError"){errMsg.push("XamlFile: ",args.xamlFile," \n Line: ",args.lineNumber," \n Position: ",args.charPosition," \n"); -}else{if(errorType=="RuntimeError"){if(args.lineNumber!=0){errMsg.push("Line: ",args.lineNumber," \n Position: ",args.charPosition," \n"); -}errMsg.push("MethodName: ",args.methodName," \n");}}throw new Error(apf.formatErrorString(0,this,errMsg.join(""))); -}function __cleanup(){for(var i=silverlightCount-1;i>=0;i--){window["__slEvent"+i]=null; -}silverlightCount=0;if(window.removeEventListener){window.removeEventListener("unload",__cleanup,false); -}else{window.detachEvent("onunload",__cleanup);}}function __getHandlerName(handler){var handlerName=""; -if(typeof handler=="string"){handlerName=handler;}else{if(typeof handler=="function"){if(silverlightCount==0){if(window.addEventListener){window.addEventListener("onunload",__cleanup,false); -}else{window.attachEvent("onunload",__cleanup);}}var count=silverlightCount++;handlerName="__slEvent"+count; -window[handlerName]=handler;}else{handlerName=null;}}return handlerName;}function onRequiredVersionAvailable(){}function onRestartRequired(){}function onUpgradeRequired(){}function onInstallRequired(){}function IsVersionAvailableOnError(sender,args){var retVal=false; -try{if(args.ErrorCode==8001&&!__installationEventFired){onUpgradeRequired();__installationEventFired=true; -}else{if(args.ErrorCode==8002&&!__installationEventFired){onRestartRequired();__installationEventFired=true; -}else{if(args.ErrorCode==5014||args.ErrorCode==2106){if(__verifySilverlight2UpgradeSuccess(args.getHost())){retVal=true; -}}else{retVal=true;}}}}catch(e){}return retVal;}function IsVersionAvailableOnLoad(sender){var retVal=false; -try{if(__verifySilverlight2UpgradeSuccess(sender.getHost())){retVal=true;}}catch(e){}return retVal; -}function __verifySilverlight2UpgradeSuccess(host){var retVal=false,version="2.0.31005",installationEvent=null; -try{if(host.IsVersionSupported(version+".99")){installationEvent=onRequiredVersionAvailable; -retVal=true;}else{if(host.IsVersionSupported(version+".0")){installationEvent=onRestartRequired; -}else{installationEvent=onUpgradeRequired;}}if(installationEvent&&!__installationEventFired){installationEvent(); -__installationEventFired=true;}}catch(e){}return retVal;}var aIsAvailable={};function isAvailable(sVersion){if(typeof sVersion=="undefined"){sVersion="1.0"; -}if(typeof aIsAvailable[sVersion]=="undefined"){aIsAvailable[sVersion]=isInstalled(sVersion); -}return aIsAvailable[sVersion];}return{onGetSilverlight:null,isBrowserRestartRequired:false,startup:startup,createObject:createObject,createObjectEx:createObjectEx,getSilverlight:getSilverlight,default_error_handler:default_error_handler,isAvailable:isAvailable}; -})();apf.setStyleRule=function(name,type,value,stylesheet,win){name=name.toLowerCase(); -if(!stylesheet){var sheets=(win||self).document.styleSheets;for(var j=sheets.length-1; -j>=0;j--){try{var rules=sheets[j][apf.styleSheetRules];for(var i=0;i=0;j--){try{var rules=sheets[j][apf.styleSheetRules];for(var i=0;i"+cssString+""); -}};apf.getStyleRecur=function(el,prop){var value=apf.hasComputedStyle?document.defaultView.getComputedStyle(el,"").getPropertyValue(prop.replace(/([A-Z])/g,function(m,m1){return"-"+m1.toLowerCase(); -})):el.currentStyle[prop];return((!value||value=="transparent"||value=="inherit")&&el.parentNode&&el.parentNode.nodeType==1)?this.getStyleRecur(el.parentNode,prop):value; -};apf.importStylesheet=function(def,win){if(!def.length){return;}var re=new RegExp("^"+document.domain,"g"); -var doc=(win||window).document;for(var index=document.styleSheets.length-1;index>=0; -index--){if(!doc.styleSheets[index].href||doc.styleSheets[index].href.match(re)){break; -}}var styleSheet=doc.styleSheets[index];function newStyleSheet(){if(doc.createStyleSheet){return doc.createStyleSheet(); -}else{var elem=doc.createElement("style");elem.type="text/css";doc.getElementsByTagName("head")[0].appendChild(elem); -return elem.sheet;}}if(!styleSheet){styleSheet=newStyleSheet();}for(var i=0;ioHtml.offsetWidth+pos[0]-10||y>oHtml.offsetHeight+pos[1]-10){return false; -}return true;};apf.getOverflowParent=function(o){o=o.offsetParent;while(o&&(this.getStyle(o,"overflow")!="hidden"||"absolute|relative".indexOf(this.getStyle(o,"position"))==-1)){o=o.offsetParent; -}return o||document.documentElement;};apf.getPositionedParent=function(o){o=o.offsetParent; -while(o&&o.tagName.toLowerCase()!="body"&&"absolute|relative".indexOf(this.getStyle(o,"position"))==-1){o=o.offsetParent; -}return o||document.documentElement;};apf.getAbsolutePosition=function(o,refParent,inclSelf){if("getBoundingClientRect" in document.documentElement){if(apf.doesNotIncludeMarginInBodyOffset&&o==document.body){return[o.offsetLeft+(parseFloat(apf.getStyle(o,"marginLeft"))||0),+(o.scrollLeft||0),o.offsetTop+(parseFloat(apf.getStyle(o,"marginTop"))||0)+(o.scrollTop||0)]; -}var box=o.getBoundingClientRect(),top=box.top,left=box.left,corr=(apf.isIE&&apf.isIE<8); -if(refParent&&refParent!=document.body){var pos=apf.getAbsolutePosition(refParent,null,true); -top-=pos[1];left-=pos[0];}if(!(apf.isIE&&o==document.documentElement)){left+=(refParent||document.body).scrollLeft||document.documentElement.scrollLeft||0; -top+=(refParent||document.body).scrollTop||document.documentElement.scrollTop||0; -}if(inclSelf&&!refParent){left+=parseInt(apf.getStyle(o,"borderLeftWidth"))||0; -top+=parseInt(apf.getStyle(o,"borderTopWidth"))||0;}return[left-(corr?2:0),top-(corr?2:0)]; -}var wt=inclSelf?0:o.offsetLeft,ht=inclSelf?0:o.offsetTop;o=inclSelf?o:o.offsetParent||o.parentNode; -if(apf.isIE8&&refParent){bw=this.getStyle(o,"borderLeftWidth");wt-=(apf.isIE&&o.currentStyle.borderLeftStyle!="none"&&bw=="medium"?2:parseInt(bw)||0); -bh=this.getStyle(o,"borderTopWidth");ht-=(apf.isIE&&o.currentStyle.borderTopStyle!="none"&&bh=="medium"?2:parseInt(bh)||0); -}var bw,bh,fl;while(o&&o!=refParent){bw=apf.isOpera||apf.isIE8?0:this.getStyle(o,"borderLeftWidth"); -wt+=(apf.isIE&&o.currentStyle.borderLeftStyle!="none"&&bw=="medium"?2:parseInt(bw)||0)+o.offsetLeft; -if(apf.isIE&&!apf.isIE8&&apf.getStyle(o,"styleFloat")=="none"&&apf.getStyle(o,"position")=="relative"){var q=o.previousSibling; -while(q){if(q.nodeType==1){fl=apf.getStyle(q,"styleFloat");if(fl=="left"){wt-=parseInt(apf.getStyle(o,"marginLeft"))||0; -break;}else{if(fl=="right"){break;}}}q=q.previousSibling;}}bh=apf.isOpera||apf.isIE8?0:this.getStyle(o,"borderTopWidth"); -ht+=(apf.isIE&&o.currentStyle.borderTopStyle!="none"&&bh=="medium"?2:parseInt(bh)||0)+o.offsetTop; -if(!apf.isGecko&&o!=refParent&&(o.tagName!="HTML"||o.ownerDocument!=document)){wt-=o.scrollLeft; -ht-=o.scrollTop;}if(o.tagName.toLowerCase()=="table"){ht-=parseInt(o.border||0)+parseInt(o.cellSpacing||0); -wt-=parseInt(o.border||0)+parseInt(o.cellSpacing||0)*2;}else{if(o.tagName.toLowerCase()=="tr"){var cp; -ht-=(cp=parseInt(o.parentNode.parentNode.cellSpacing));while(o.previousSibling){ht-=(o=o.previousSibling).offsetHeight+cp; -}}}if(apf.isIE&&!o.offsetParent&&o.parentNode.nodeType==1){wt-=o.parentNode.scrollLeft; -ht-=o.parentNode.scrollTop;}o=o.offsetParent;}return[wt,ht];};apf.getHorBorders=function(oHtml){return Math.max(0,(parseInt(apf.getStyle(oHtml,"borderLeftWidth"))||0)+(parseInt(apf.getStyle(oHtml,"borderRightWidth"))||0)); -};apf.getVerBorders=function(oHtml){return Math.max(0,(parseInt(apf.getStyle(oHtml,"borderTopWidth"))||0)+(parseInt(apf.getStyle(oHtml,"borderBottomWidth"))||0)); -};apf.getWidthDiff=function(oHtml){if(apf.hasFlexibleBox&&apf.getStyle(oHtml,apf.CSSPREFIX+"BoxSizing")!="content-box"){return 0; -}return Math.max(0,(parseInt(apf.getStyle(oHtml,"paddingLeft"))||0)+(parseInt(apf.getStyle(oHtml,"paddingRight"))||0)+(parseInt(apf.getStyle(oHtml,"borderLeftWidth"))||0)+(parseInt(apf.getStyle(oHtml,"borderRightWidth"))||0)); -};apf.getHeightDiff=function(oHtml){if(apf.hasFlexibleBox&&apf.getStyle(oHtml,apf.CSSPREFIX+"BoxSizing")!="content-box"){return 0; -}return Math.max(0,(parseInt(apf.getStyle(oHtml,"paddingTop"))||0)+(parseInt(apf.getStyle(oHtml,"paddingBottom"))||0)+(parseInt(apf.getStyle(oHtml,"borderTopWidth"))||0)+(parseInt(apf.getStyle(oHtml,"borderBottomWidth"))||0)); -};apf.getDiff=function(oHtml){if(apf.hasFlexibleBox&&apf.getStyle(oHtml,apf.CSSPREFIX+"BoxSizing")!="content-box"){return[0,0]; -}return[Math.max(0,(parseInt(apf.getStyle(oHtml,"paddingLeft"))||0)+(parseInt(apf.getStyle(oHtml,"paddingRight"))||0)+(parseInt(apf.getStyle(oHtml,"borderLeftWidth"))||0)+(parseInt(apf.getStyle(oHtml,"borderRightWidth"))||0)),Math.max(0,(parseInt(apf.getStyle(oHtml,"paddingTop"))||0)+(parseInt(apf.getStyle(oHtml,"paddingBottom"))||0)+(parseInt(apf.getStyle(oHtml,"borderTopWidth"))||0)+(parseInt(apf.getStyle(oHtml,"borderBottomWidth"))||0))]; -};apf.getMargin=function(oHtml){return[(parseInt(apf.getStyle(oHtml,"marginLeft"))||0)+(parseInt(apf.getStyle(oHtml,"marginRight"))||0),(parseInt(apf.getStyle(oHtml,"marginTop"))||0)+(parseInt(apf.getStyle(oHtml,"marginBottom"))||0)]; -};apf.getHtmlInnerWidth=function(oHtml){return(oHtml.offsetWidth-(parseInt(apf.getStyle(oHtml,"borderLeftWidth"))||0)-(parseInt(apf.getStyle(oHtml,"borderRightWidth"))||0)); -};apf.getHtmlInnerHeight=function(oHtml){return(oHtml.offsetHeight-(parseInt(apf.getStyle(oHtml,"borderTopWidth"))||0)-(parseInt(apf.getStyle(oHtml,"borderBottomWidth"))||0)); -};apf.getViewPort=function(win){win=win||window;var doc=(!win.document.compatMode||win.document.compatMode=="CSS1Compat")?win.document.html||win.document.documentElement:win.document.body; -return{x:win.pageXOffset||doc.scrollLeft,y:win.pageYOffset||doc.scrollTop,width:win.innerWidth||doc.clientWidth,height:win.innerHeight||doc.clientHeight}; -};apf.highlightXml=apf.highlightCode=function(strCode){var lines=strCode.split(/\n\r?/); -for(var min=1000,i=0,l=lines.length;i||<\?([\w\-]+)([\s\S]+?)\?>|<\!\[CDATA\[([\s\S]*?)\]\]>|<(\w+:)?script([\s\S]*?)>([\s\S]*?)<\/(?:\w+:)?script>|<([\/\w\:\-]+)([\s\S]*?)(\/?)>/g,function(m,doctype,comment,ptarget,pcontents,cdata,sprefix,sattr,scontent,tagName,attrs,bl){if(doctype){return'<!doctype'+doctype.replace(/"; -}else{if(comment){return'<!--'+comment.replace(/"; -}else{if(ptarget){return'<?'+ptarget+""+apf.highlightJs(pcontents,true)+'?>'; -}else{if(cdata){return'<![CDATA['+cdata+"]]>"; -}else{if(sprefix){return'<'+sprefix+"script"+(attrs?""+attrs.replace(/("[\s\S]*?")|([\w\-\:]+)/g,function(m,s,w){if(s){return s; -}return''+w+"";})+'>':">")+apf.highlightJs(scontent,true)+'</'+sprefix+"script>"; -}else{if(tagName){return'<'+(tagName.substr(0,2)=="a:"?""+tagName+"":tagName)+(attrs?""+attrs.replace(/("[\s\S]*?")|([\w\-\:]+)/g,function(m,s,w){if(s){return s; -}return''+w+"";})+''+bl+">":">"); -}}}}}}});return strCode;};apf.removeStartWhitespaces=function(strCode){var lines=strCode.split(/\n\r?/); -for(var min=1000,i=0,l=lines.length;i"+strCode+"",null,true); -var script=[],bool={"true":1,"false":1};var x=(function recur(nodes,depth,paout){var pre=new Array(depth+2).join(" "),output=[]; -for(var node,i=0,l=nodes.length;i'+f+"
"+fws; -}else{if(co||colong){return''+(co||colong)+""; -}else{if(str1||str2){return''+(str1||str2)+""; -}else{if(nw){return nw+''+kw+""+nw2;}}}}});}; -apf.highlightCode2=function(strCode){var comment=[],str=[];return strCode.replace(/(\/\*[\s\S]*?\*\/|\/\/.*)/g,function(a){comment.push(a); -return"###n"+(comment.length-1)+"###";}).replace(/\"([\s\S]*?)\"/g,function(a,b){str.push(b); -return"###s"+(str.length-1)+"###";}).replace(/\'([\s\S]*?)\'/g,function(a,b){str.push(b); -return"###q"+(str.length-1)+"###";}).replace(/(\<)|(\>)/g,function(n,a,b){return""+(a?"@lt@":"@gt@")+""; -}).replace(/(\W)-?([\d\.]+)(\W)/g,"$1$2$3").replace(/([\|\&\=\;\,\:\?\+\*\-]+)/g,"$1").replace(/(\W)(break|continue|do|for|import|new|this|void|case|default|else|function|in|return|typeof|while|comment|delete|export|if|label|switch|var|with|abstract|implements|protected|boolean|instanceOf|public|byte|int|short|char|interface|static|double|long|synchronized|false|native|throws|final|null|transient|float|package|true|goto|private|catch|enum|throw|class|extends|try|const|finally|debugger|super)(\W)/g,"$1$2$3").replace(/([\(\)\{\}\[\]])/g,"$1").replace(/###n(\d+)###/g,function(a,b){return""+comment[b].escapeHTML()+""; -}).replace(/###s(\d+)###/g,function(a,b){return'"'+str[b].escapeHTML()+'"'; -}).replace(/###q(\d+)###/g,function(a,b){return"'"+str[b].escapeHTML()+"'"; -}).replace(/stylecolor(.*?)\>/g,"style='color:$1'>").replace(/@(.*?)@/g,"&$1;"); -};apf.formatJS=function(strJs){var d=0,r=0;var comment=[],str=[];return strJs.replace(/(\/\*[\s\S]*?\*\/|\/\/.*)/g,function(a){comment.push(a); -return"###n"+(comment.length-1)+"###";}).replace(/\"([\s\S]*?)\"/g,function(a,b){str.push(b); -return"###s"+(str.length-1)+"###";}).replace(/\'([\s\S]*?)\'/g,function(a,b){str.push(b); -return"###q"+(str.length-1)+"###";}).replace(/;+/g,";").replace(/{;/g,"{").replace(/({)|(})|(\()|(\))|(;)/g,function(m,co,cc,ro,rc,e){if(co){d++; -}if(cc){d--;}if(ro){r++;return ro;}if(rc){r--;return rc;}var o="";for(var i=0;i0)?e:(";\n"+o); -}}).replace(/;\s*\n\s*\n/g,";\n").replace(/}var/g,"}\nvar").replace(/([\n\s]*)###n(\d+)###[\n\s]*/g,function(a,b,c){return b+comment[c]+b; -}).replace(/###s(\d+)###/g,function(a,b,c){return'"'+str[b]+'"';}).replace(/###q(\d+)###/g,function(a,b,c){return"'"+str[b]+"'"; -});};apf.diff_match_patch=new (function(){this.diffTimeout=1;this.diffEditCost=4; -this.diffDualThreshold=32;this.matchThreshold=0.5;this.matchDistance=1000;this.patchDeleteThreshold=0.5; -this.patchMargin=4;var DIFF_DELETE=-1,DIFF_INSERT=1,DIFF_EQUAL=0,getMaxBits=function(){var maxbits=0,oldi=1,newi=2; -while(oldi!=newi){maxbits++;oldi=newi;newi=newi<<1;}return maxbits;},patch_obj=function(){this.diffs=[]; -this.start1=null;this.start2=null;this.length1=0;this.length2=0;},opMap=[];opMap[DIFF_INSERT]="+"; -opMap[DIFF_DELETE]="-";opMap[DIFF_EQUAL]=" ";patch_obj.prototype.toString=function(){var coords1=(this.length1===0)?this.start1+",0":(this.length1==1)?this.start1+1:(this.start1+1)+","+this.length1,coords2=(this.length2===0)?this.start2+",0":(this.length2==1)?this.start2+1:(this.start2+1)+","+this.length2,text=["@@ -"+coords1+" +"+coords2+" @@\n"],x=0,l=this.diffs.length; -for(;xtext2.length?text1:text2,shorttext=text1.length>text2.length?text2:text1,i=longtext.indexOf(shorttext); -if(i!=-1){diffs=[[DIFF_INSERT,longtext.substring(0,i)],[DIFF_EQUAL,shorttext],[DIFF_INSERT,longtext.substring(i+shorttext.length)]]; -if(text1.length>text2.length){diffs[0][0]=diffs[2][0]=DIFF_DELETE;}return diffs; -}longtext=shorttext=null;var a,linearray,hm=this.diff_halfMatch(text1,text2);if(hm){return this.diff_main(hm[0],hm[2],checklines).concat([[DIFF_EQUAL,hm[4]]],this.diff_main(hm[1],hm[3],checklines)); -}if(checklines&&(text1.length<100||text2.length<100)){checklines=false;}if(checklines){a=this.diff_linesToChars(text1,text2); -text1=a[0];text2=a[1];linearray=a[2];}diffs=this.diff_map(text1,text2);if(!diffs){diffs=[[DIFF_DELETE,text1],[DIFF_INSERT,text2]]; -}if(checklines){this.diff_charsToLines(diffs,linearray);this.diff_cleanupSemantic(diffs); -diffs.push([DIFF_EQUAL,""]);var pointer=0,count_delete=0,count_insert=0,text_delete="",text_insert=""; -while(pointer=1&&count_insert>=1){a=this.diff_main(text_delete,text_insert,false); -diffs.splice(pointer-count_delete-count_insert,count_delete+count_insert);pointer=pointer-count_delete-count_insert; -for(var j=a.length-1;j>=0;j--){diffs.splice(pointer,0,a[j]);}pointer=pointer+a.length; -}count_insert=0;count_delete=0;text_delete="";text_insert="";break;}pointer++;}diffs.pop(); -}return diffs;};this.diff_linesToChars=function(text1,text2){var lineArray=[""],lineHash={}; -function diff_linesToCharsMunge(text){var chars="",lineStart=0,lineEnd=-1,lineArrayLength=lineArray.length; -while(lineEnd0&&(new Date()).getTime()>ms_end){return null; -}v_map1[d]={};for(k=-d;k<=d;k+=2){if(k==-d||k!=d&&v1[k-1]=0;d--){while(1){if(v_map[d].hasOwnProperty?v_map[d].hasOwnProperty((x-1)+","+y):(v_map[d][(x-1)+","+y]!==undefined)){x--; -if(last_op===DIFF_DELETE){path[0][1]=text1.charAt(x)+path[0][1];}else{path.unshift([DIFF_DELETE,text1.charAt(x)]); -}last_op=DIFF_DELETE;break;}else{if(v_map[d].hasOwnProperty?v_map[d].hasOwnProperty(x+","+(y-1)):(v_map[d][x+","+(y-1)]!==undefined)){y--; -if(last_op===DIFF_INSERT){path[0][1]=text2.charAt(y)+path[0][1];}else{path.unshift([DIFF_INSERT,text2.charAt(y)]); -}last_op=DIFF_INSERT;break;}else{x--;y--;if(last_op===DIFF_EQUAL){path[0][1]=text1.charAt(x)+path[0][1]; -}else{path.unshift([DIFF_EQUAL,text1.charAt(x)]);}last_op=DIFF_EQUAL;}}}}return path; -};this.diff_path2=function(v_map,text1,text2){var path=[],pathLength=0,x=text1.length,y=text2.length,last_op=null,d=v_map.length-2; -for(;d>=0;d--){while(1){if(v_map[d].hasOwnProperty?v_map[d].hasOwnProperty((x-1)+","+y):(v_map[d][(x-1)+","+y]!==undefined)){x--; -if(last_op===DIFF_DELETE){path[pathLength-1][1]+=text1.charAt(text1.length-x-1); -}else{path[pathLength++]=[DIFF_DELETE,text1.charAt(text1.length-x-1)];}last_op=DIFF_DELETE; -break;}else{if(v_map[d].hasOwnProperty?v_map[d].hasOwnProperty(x+","+(y-1)):(v_map[d][x+","+(y-1)]!==undefined)){y--; -if(last_op===DIFF_INSERT){path[pathLength-1][1]+=text2.charAt(text2.length-y-1); -}else{path[pathLength++]=[DIFF_INSERT,text2.charAt(text2.length-y-1)];}last_op=DIFF_INSERT; -break;}else{x--;y--;if(last_op===DIFF_EQUAL){path[pathLength-1][1]+=text1.charAt(text1.length-x-1); -}else{path[pathLength++]=[DIFF_EQUAL,text1.charAt(text1.length-x-1)];}last_op=DIFF_EQUAL; -}}}}return path;};this.diff_commonPrefix=function(text1,text2){if(!text1||!text2||text1.charCodeAt(0)!==text2.charCodeAt(0)){return 0; -}var pointermin=0,pointermax=Math.min(text1.length,text2.length),pointermid=pointermax,pointerstart=0; -while(pointermintext2.length?text1:text2,shorttext=text1.length>text2.length?text2:text1,_self=this; -if(longtext.length<10||shorttext.length<1){return null;}function diff_halfMatchI(longtext,shorttext,i){var seed=longtext.substring(i,i+Math.floor(longtext.length/4)),j=-1,best_common="",best_longtext_a,best_longtext_b,best_shorttext_a,best_shorttext_b,prefixLength,suffixLength; -while((j=shorttext.indexOf(seed,j+1))!=-1){prefixLength=_self.diff_commonPrefix(longtext.substring(i),shorttext.substring(j)); -suffixLength=_self.diff_commonSuffix(longtext.substring(0,i),shorttext.substring(0,j)); -if(best_common.length=longtext.length/2){return[best_longtext_a,best_longtext_b,best_shorttext_a,best_shorttext_b,best_common]; -}else{return null;}}var hm1=diff_halfMatchI(longtext,shorttext,Math.ceil(longtext.length/4)),hm2=diff_halfMatchI(longtext,shorttext,Math.ceil(longtext.length/2)),hm; -if(!hm1&&!hm2){return null;}else{if(!hm2){hm=hm1;}else{if(!hm1){hm=hm2;}else{hm=hm1[4].length>hm2[4].length?hm1:hm2; -}}}var text1_a,text1_b,text2_a,text2_b;if(text1.length>text2.length){text1_a=hm[0]; -text1_b=hm[1];text2_a=hm[2];text2_b=hm[3];}else{text2_a=hm[0];text2_b=hm[1];text1_a=hm[2]; -text1_b=hm[3];}return[text1_a,text1_b,text2_a,text2_b,hm[4]];};this.diff_cleanupSemantic=function(diffs){var changes=false,equalities=[],equalitiesLength=0,lastequality=null,pointer=0,length_changes1=0,length_changes2=0; -while(pointer0?equalities[equalitiesLength-1]:-1;length_changes1=0; -length_changes2=0;lastequality=null;changes=true;}}pointer++;}if(changes){this.diff_cleanupMerge(diffs); -}this.diff_cleanupSemanticLossless(diffs);};this.diff_cleanupSemanticLossless=function(diffs){var punctuation=/[^a-zA-Z0-9]/,whitespace=/\s/,linebreak=/[\r\n]/,blanklineEnd=/\n\r?\n$/,blanklineStart=/^\r?\n\r?\n/; -function diff_cleanupSemanticScore(one,two){if(!one||!two){return 5;}var score=0; -if(one.charAt(one.length-1).match(punctuation)||two.charAt(0).match(punctuation)){score++; -if(one.charAt(one.length-1).match(whitespace)||two.charAt(0).match(whitespace)){score++; -if(one.charAt(one.length-1).match(linebreak)||two.charAt(0).match(linebreak)){score++; -if(one.match(blanklineEnd)||two.match(blanklineStart)){score++;}}}}return score; -}var pointer=1,equality1,edit,equality2,commonOffset,commonString,bestEquality1,bestEdit,bestEquality2,bestScore,score; -while(pointer=bestScore){bestScore=score;bestEquality1=equality1;bestEdit=edit;bestEquality2=equality2; -}}if(diffs[pointer-1][1]!=bestEquality1){if(bestEquality1){diffs[pointer-1][1]=bestEquality1; -}else{diffs.splice(pointer-1,1);pointer--;}diffs[pointer][1]=bestEdit;if(bestEquality2){diffs[pointer+1][1]=bestEquality2; -}else{diffs.splice(pointer+1,1);pointer--;}}}pointer++;}};this.diff_cleanupEfficiency=function(diffs){var changes=false,equalities=[],equalitiesLength=0,lastequality="",pointer=0,pre_ins=false,pre_del=false,post_ins=false,post_del=false; -while(pointer0?equalities[equalitiesLength-1]:-1;post_ins=post_del=false; -}changes=true;}}pointer++;}if(changes){this.diff_cleanupMerge(diffs);}};this.diff_cleanupMerge=function(diffs){diffs.push([DIFF_EQUAL,""]); -var pointer=0,count_delete=0,count_insert=0,text_delete="",text_insert="",commonlength; -while(pointer0&&diffs[pointer-count_delete-count_insert-1][0]==DIFF_EQUAL){diffs[pointer-count_delete-count_insert-1][1]+=text_insert.substring(0,commonlength); -}else{diffs.splice(0,0,[DIFF_EQUAL,text_insert.substring(0,commonlength)]);pointer++; -}text_insert=text_insert.substring(commonlength);text_delete=text_delete.substring(commonlength); -}commonlength=this.diff_commonSuffix(text_insert,text_delete);if(commonlength!==0){diffs[pointer][1]=text_insert.substring(text_insert.length-commonlength)+diffs[pointer][1]; -text_insert=text_insert.substring(0,text_insert.length-commonlength);text_delete=text_delete.substring(0,text_delete.length-commonlength); -}}if(count_delete===0){diffs.splice(pointer-count_delete-count_insert,count_delete+count_insert,[DIFF_INSERT,text_insert]); -}else{if(count_insert===0){diffs.splice(pointer-count_delete-count_insert,count_delete+count_insert,[DIFF_DELETE,text_delete]); -}else{diffs.splice(pointer-count_delete-count_insert,count_delete+count_insert,[DIFF_DELETE,text_delete],[DIFF_INSERT,text_insert]); -}}pointer=pointer-count_delete-count_insert+(count_delete?1:0)+(count_insert?1:0)+1; -}else{if(pointer!==0&&diffs[pointer-1][0]==DIFF_EQUAL){diffs[pointer-1][1]+=diffs[pointer][1]; -diffs.splice(pointer,1);}else{pointer++;}}count_insert=0;count_delete=0;text_delete=""; -text_insert="";break;}}if(diffs[diffs.length-1][1]===""){diffs.pop();}var changes=false; -pointer=1;while(pointerloc){break;}last_chars1=chars1;last_chars2=chars2;}if(diffs.length!=x&&diffs[x][0]===DIFF_DELETE){return last_chars2; -}return last_chars2+(loc-last_chars1);};this.diff_prettyHtml=function(diffs){var html=[],i=0,x=0,l=diffs.length,op,data,text; -for(;x/g,">").replace(/\n/g,"¶
"); -switch(op){case DIFF_INSERT:html[x]=''+text+""; -break;case DIFF_DELETE:html[x]=''+text+""; -break;case DIFF_EQUAL:html[x]=''+text+"";break;}if(op!==DIFF_DELETE){i+=data.length; -}}return html.join("");};this.diff_text1=function(diffs){var text=[],x=0,l=diffs.length; -for(;xthis.matchMaxBits){throw new Error("Pattern too long for this browser."); -}var s=this.match_alphabet(pattern),_self=this;function match_bitapScore(e,x){var accuracy=e/pattern.length,proximity=Math.abs(loc-x); -if(!_self.matchDistance){return proximity?1:accuracy;}return accuracy+(proximity/_self.matchDistance); -}var score_threshold=this.matchThreshold,best_loc=text.indexOf(pattern,loc);if(best_loc!=-1){score_threshold=Math.min(match_bitapScore(0,best_loc),score_threshold); -}best_loc=text.lastIndexOf(pattern,loc+pattern.length);if(best_loc!=-1){score_threshold=Math.min(match_bitapScore(0,best_loc),score_threshold); -}best_loc=-1;var matchmask=1<<(pattern.length-1),bin_max=pattern.length+text.length,d=0,l=pattern.length,bin_min,bin_mid,last_rd,start,finish,rd,j,charMatch,score; -for(;d=start;j--){charMatch=s[text.charAt(j-1)]; -if(d===0){rd[j]=((rd[j+1]<<1)|1)&charMatch;}else{rd[j]=((rd[j+1]<<1)|1)&charMatch|(((last_rd[j+1]|last_rd[j])<<1)|1)|last_rd[j+1]; -}if(rd[j]&matchmask){score=match_bitapScore(d,j-1);if(score<=score_threshold){score_threshold=score; -best_loc=j-1;if(best_loc>loc){start=Math.max(1,2*loc-best_loc);}else{break;}}}}if(match_bitapScore(d+1,loc)>score_threshold){break; -}last_rd=rd;}return best_loc;};this.match_alphabet=function(pattern){var s={},i=0,l=pattern.length; -for(;i2){this.diff_cleanupSemantic(diffs); -this.diff_cleanupEfficiency(diffs);}}else{if(typeof a=="object"&&typeof opt_b=="undefined"&&typeof opt_c=="undefined"){diffs=a; -text1=this.diff_text1(diffs);}else{if(typeof a=="string"&&typeof opt_b=="object"&&typeof opt_c=="undefined"){text1=a; -diffs=opt_b;}else{if(typeof a=="string"&&typeof opt_b=="string"&&typeof opt_c=="object"){text1=a; -diffs=opt_c;}else{throw new Error("Unknown call format to patch_make.");}}}}if(diffs.length===0){return[]; -}var patches=[],patch=new patch_obj(),patchDiffLength=0,char_count1=0,char_count2=0,prepatch_text=text1,postpatch_text=text1,x=0,l=diffs.length,diff_type,diff_text; -for(;x=2*this.patchMargin){if(patchDiffLength){this.patch_addContext(patch,prepatch_text); -patches.push(patch);patch=new patch_obj();patchDiffLength=0;prepatch_text=postpatch_text; -char_count1=char_count2;}}}break;}if(diff_type!==DIFF_INSERT){char_count1+=diff_text.length; -}if(diff_type!==DIFF_DELETE){char_count2+=diff_text.length;}}if(patchDiffLength){this.patch_addContext(patch,prepatch_text); -patches.push(patch);}return patches;};var patch_deepCopy=function(patches){var patchesCopy=[],x=0,l=patches.length,patch,patchCopy,y; -for(;xthis.matchMaxBits){start_loc=this.match_main(text,text1.substring(0,this.matchMaxBits),expected_loc); -if(start_loc!=-1){end_loc=this.match_main(text,text1.substring(text1.length-this.matchMaxBits),expected_loc+text1.length-this.matchMaxBits); -if(end_loc==-1||start_loc>=end_loc){start_loc=-1;}}}else{start_loc=this.match_main(text,text1,expected_loc); -}if(start_loc==-1){results[x]=false;delta-=patch.length2-patch.length1;}else{results[x]=true; -delta=start_loc-expected_loc;if(end_loc==-1){text2=text.substring(start_loc,start_loc+text1.length); -}else{text2=text.substring(start_loc,end_loc+this.matchMaxBits);}if(text1==text2){text=text.substring(0,start_loc)+this.diff_text2(patch.diffs)+text.substring(start_loc+text1.length); -}else{diffs=this.diff_main(text1,text2,false);if(text1.length>this.matchMaxBits&&this.diff_levenshtein(diffs)/text1.length>this.patchDeleteThreshold){results[x]=false; -}else{this.diff_cleanupSemanticLossless(diffs);index1=0;for(y=0,l2=patch.diffs.length; -ydiffs[0][1].length){extraLength=paddingLength-diffs[0][1].length; -diffs[0][1]=nullPadding.substring(diffs[0][1].length)+diffs[0][1];patch.start1-=extraLength; -patch.start2-=extraLength;patch.length1+=extraLength;patch.length2+=extraLength; -}}patch=patches[patches.length-1];diffs=patch.diffs;if(diffs.length==0||diffs[diffs.length-1][0]!=DIFF_EQUAL){diffs.push([DIFF_EQUAL,nullPadding]); -patch.length1+=paddingLength;patch.length2+=paddingLength;}else{if(paddingLength>diffs[diffs.length-1][1].length){extraLength=paddingLength-diffs[diffs.length-1][1].length; -diffs[diffs.length-1][1]+=nullPadding.substring(0,extraLength);patch.length1+=extraLength; -patch.length2+=extraLength;}}return nullPadding;};this.patch_splitMax=function(patches){var x=0,bigpatch,patch_size,start1,start2,precontext,patch,empty,diff_type,diff_text,postcontext; -for(;x2*patch_size){patch.length1+=diff_text.length; -start1+=diff_text.length;empty=false;patch.diffs.push([diff_type,diff_text]);bigpatch.diffs.shift(); -}else{diff_text=diff_text.substring(0,patch_size-patch.length1-this.patchMargin); -patch.length1+=diff_text.length;start1+=diff_text.length;if(diff_type===DIFF_EQUAL){patch.length2+=diff_text.length; -start2+=diff_text.length;}else{empty=false;}patch.diffs.push([diff_type,diff_text]); -if(diff_text==bigpatch.diffs[0][1]){bigpatch.diffs.shift();}else{bigpatch.diffs[0][1]=bigpatch.diffs[0][1].substring(diff_text.length); -}}}}precontext=this.diff_text2(patch.diffs).substring(precontext.length-this.patchMargin); -postcontext=this.diff_text1(bigpatch.diffs).substring(0,this.patchMargin);if(postcontext!==""){patch.length1+=postcontext.length; -patch.length2+=postcontext.length;if(patch.diffs.length!==0&&patch.diffs[patch.diffs.length-1][0]===DIFF_EQUAL){patch.diffs[patch.diffs.length-1][1]+=postcontext; -}else{patch.diffs.push([DIFF_EQUAL,postcontext]);}}if(!empty){patches.splice(++x,0,patch); -}}}};this.patch_toText=function(patches){var text=[],x=0,l=patches.length;for(; -x").replace(/&/g,"&").replace(/ /g," "); -};apf.isCharacter=function(charCode){return(charCode<112||charCode>122)&&(charCode==32||charCode>42||charCode==8); -};apf.randomGenerator={d:new Date(),seed:null,A:48271,M:2147483647,Q:null,R:null,oneOverM:null,generate:function(lnr,unr){if(this.seed==null){this.seed=2345678901+(this.d.getSeconds()*16777215)+(this.d.getMinutes()*65535); -}this.Q=this.M/this.A;this.R=this.M%this.A;this.oneOverM=1/this.M;return Math.floor((unr-lnr+1)*this.next()+lnr); -},next:function(){var hi=this.seed/this.Q;var lo=this.seed%this.Q;var test=this.A*lo-this.R*hi; -if(test>0){this.seed=test;}else{this.seed=test+this.M;}return(this.seed*this.oneOverM); -}};apf.getNoCacheUrl=function(url){return url+(url.indexOf("?")==-1?"?":"&")+"nocache="+new Date().getTime(); -};apf.parseExpression=function(str){if(!apf.parseExpression.regexp.test(str)){return str; -}return eval(RegExp.$1);};apf.parseExpression.regexp=/^\{([\s\S]*)\}$/;apf.formatNumber=function(num,prefix){var nr=parseFloat(num); -if(!nr){return num;}var str=new String(Math.round(nr*100)/100).replace(/(\.\d?\d?)$/,function(m1){return m1.pad(3,"0",apf.PAD_RIGHT); -});if(str.indexOf(".")==-1){str+=".00";}return prefix+str;};apf.jsexec=function(str,win){if(!str){return str; -}if(!win){win=self;}if(apf.isO3){eval(str,self);}else{if(apf.hasExecScript){win.execScript(str); -}else{var head=win.document.getElementsByTagName("head")[0];if(head){var script=win.document.createElement("script"); -script.setAttribute("type","text/javascript");script.text=str;head.appendChild(script); -head.removeChild(script);}else{eval(str,win);}}}return str;};apf.K=function(){}; -apf.isNumber=function(value){return parseFloat(value)==value;};apf.isArray=function(value){return Object.prototype.toString.call(value)==="[object Array]"; -};apf.isTrue=function(c){return(c===true||c==="true"||c==="on"||typeof c=="number"&&c>0||c==="1"); -};apf.isFalse=function(c){return(c===false||c==="false"||c==="off"||c===0||c==="0"); -};apf.isNot=function(c){return(!c&&typeof c!="string"&&c!==0||(typeof c=="number"&&!isFinite(c))); -};apf.removePathContext=function(base,url){if(!url){return"";}if(url.indexOf(base)>-1){return url.substr(base.length); -}return url;};apf.cancelBubble=function(e,o,noPropagate){if(e.stopPropagation){e.stopPropagation(); -}else{e.cancelBubble=true;}if(!noPropagate){if(o&&o.$ext&&o.$ext["on"+(e.type||e.name)]){o.$ext["on"+(e.type||e.name)](e); -}apf.window.$mousedown(e);}};apf.destroyHtmlNode=function(element){if(!element){return; -}if(!apf.isIE||element.ownerDocument!=document){if(element.parentNode){element.parentNode.removeChild(element); -}return;}var garbageBin=document.getElementById("IELeakGarbageBin");if(!garbageBin){garbageBin=document.createElement("DIV"); -garbageBin.id="IELeakGarbageBin";garbageBin.style.display="none";document.body.appendChild(garbageBin); -}garbageBin.appendChild(element);garbageBin.innerHTML="";};apf.getRules=function(node){var rules={}; -for(var w=node.firstChild;w;w=w.nextSibling){if(w.nodeType!=1){continue;}else{if(!rules[w[apf.TAGNAME]]){rules[w[apf.TAGNAME]]=[]; -}rules[w[apf.TAGNAME]].push(w);}}return rules;};apf.isCoord=function(n){return n||n===0; -};apf.getCoord=function(n,other){return n||n===0?n:other;};apf.getBox=function(value,base){if(!base){base=0; -}if(value==null||(!parseInt(value)&&parseInt(value)!=0)){return[0,0,0,0];}var x=String(value).splitSafe(" "); -for(var i=0;i1){tree.shift();data=this.getNode(data,tree);}return data;}nc++; -}}}return null;};apf.getFirstElement=function(xmlNode){return xmlNode.firstChild.nodeType==1?xmlNode.firstChild:xmlNode.firstChild.nextSibling; -};apf.getLastElement=function(xmlNode){return xmlNode.lastChild.nodeType==1?xmlNode.lastChild:xmlNode.lastChild.previousSibling; -};apf.selectTextHtml=function(oHtml){if(!apf.hasMsRangeObject){return;}var r=document.selection.createRange(); -try{r.moveToElementText(oHtml);}catch(e){}r.select();};apf.visibilitymanager=function(){var tree={}; -var _self=this;var inited=false;this.check=function(amlNode,type,callback){if(amlNode.$ext.offsetHeight||amlNode.$ext.offsetWidth){return true; -}if(amlNode.$visibleCheck){if(amlNode.$visibleCheck[type]){return;}}else{amlNode.$visibleCheck={}; -}function cleanup(setInsertion){var p=amlNode;while(p){p.removeEventListener("prop.visible",check); -p.removeEventListener("DOMNodeRemoved",remove);p.removeEventListener("DOMNodeRemovedFromDocument",remove); -if(setInsertion){p.addEventListener("DOMNodeInserted",add);}p=p.parentNode||p.$parentNode; -}delete amlNode.$visibleCheck[type];}function check(e){if(!amlNode.$ext.offsetHeight&&!amlNode.$ext.offsetWidth){return; -}callback.call(amlNode);cleanup();}function remove(e){if(e.currentTarget!=this){return; -}cleanup(e.name=="DOMNodeRemoved");}function add(){var p=amlNode;while(p){p.addEventListener("prop.visible",check); -p.addEventListener("DOMNodeRemoved",remove);p.addEventListener("DOMNodeRemovedFromDocument",remove); -p.removeEventListener("DOMNodeInserted",add);p=p.parentNode||p.$parentNode;}amlNode.$visibleCheck[type]=true; -}add();return false;};this.permanent=function(amlNode,show,hide){var state=amlNode.$ext&&(amlNode.$ext.offsetHeight||amlNode.$ext.offsetWidth); -function check(e){var newState=amlNode.$ext&&(amlNode.$ext.offsetHeight||amlNode.$ext.offsetWidth); -if(newState==state){return;}if(newState){show();}else{hide();}state=newState;}function cleanup(setInsertion){var p=amlNode; -while(p){p.removeEventListener("prop.visible",check);p.removeEventListener("DOMNodeRemoved",remove); -p.removeEventListener("DOMNodeRemovedFromDocument",remove);if(setInsertion){p.addEventListener("DOMNodeInserted",add); -}p=p.parentNode||p.$parentNode;}check();}function remove(e){if(e.currentTarget!=this){return; -}cleanup(e.name=="DOMNodeRemoved");}function add(){var p=amlNode;while(p){p.addEventListener("prop.visible",check); -p.addEventListener("DOMNodeRemoved",remove);p.addEventListener("DOMNodeRemovedFromDocument",remove); -p.removeEventListener("DOMNodeInserted",add);p=p.parentNode||p.$parentNode;}check(); -}add();return state;};this.removePermanent=function(amlNode){};};apf.isChildOf=function(pNode,childnode,orItself){if(!pNode||!childnode){return false; -}if(childnode.nodeType==2){childnode=childnode.ownerElement||childnode.selectSingleNode(".."); -}if(orItself&&pNode==childnode){return true;}var loopnode=childnode.parentNode; -while(loopnode){if(loopnode==pNode){return true;}loopnode=loopnode.parentNode;}return false; -};apf.escapeXML=function(str){if(typeof str!=="string"){return str;}return((str||"").replace(/&/g,"&").replace(/"/g,""").replace(//g,">").replace(/'/g,"'")); -};apf.isOnlyChild=function(node,nodeType){if(!node||!node.parentNode||nodeType&&nodeType.indexOf(node.nodeType)==-1){return false; -}var i,l,cnode,nodes=node.parentNode.childNodes;for(i=0,l=nodes.length;i=0;i--){var s=nodes[i].getAttribute(apf.xmldb.xmlListenTag).split(";"); -for(j=s.length-1;j>=0;j--){node=apf.all[s[j]];if(!node){continue;}if(node.dataParent&&node.dataParent.xpath){node.dataParent.parent.signalXmlUpdate[node.$uniqueId]=true; -}else{if(node.$model){node.$model.$waitForXml(node);}}}}nodes=parentNode.childNodes; -for(i=nodes.length-1;i>=0;i--){parentNode.removeChild(nodes[i]);}}if(options&&options.start){var reserved,beforeNode,nodes,doc,i,l,marker=options.marker; -if(!marker){}if(marker.getAttribute("start")-options.start==0){marker.setAttribute("start",options.start+options.length); -reserved=parseInt(marker.getAttribute("reserved"));marker.setAttribute("reserved",reserved+options.length); -beforeNode=marker;}else{if(options.start+options.length==marker.getAttribute("end")){marker.setAttribute("end",options.start+options.length); -beforeNode=marker.nextSibling;reserved=parseInt(marker.getAttribute("reserved"))+parseInt(marker.getAttribute("end"))-options.length; -}else{var m2=marker.parentNode.insertBefore(marker.cloneNode(true),marker);m2.setAttribute("end",options.start-1); -marker.setAttribute("start",options.start+options.length);reserved=parseInt(marker.getAttribute("reserved")); -marker.setAttribute("reserved",reserved+options.length);beforeNode=marker;}}nodes=XMLRoot.childNodes; -if(parentNode.ownerDocument.importNode){doc=parentNode.ownerDocument;for(i=0,l=nodes.length; -i=0;i--){parentNode.insertBefore(nodes[0],beforeNode).setAttribute(apf.xmldb.xmlIdTag,options.documentId+"|"+(reserved+i)); -}}}else{beforeNode=options&&options.beforeNode?options.beforeNode:apf.getNode(parentNode,[0]); -nodes=XMLRoot.childNodes;if(parentNode.ownerDocument.importNode){doc=parentNode.ownerDocument; -for(i=0,l=nodes.length;i=0;i--){parentNode.insertBefore(nodes[0],beforeNode); -}}}if(options&&options.copyAttributes){var attr=XMLRoot.attributes;for(i=0;i0?nodeList[0]:null;};apf.getInheritedAttribute=function(xml,attr,func){var result,avalue; -if(xml.nodeType!=1){xml=xml.parentNode;}while(xml&&(xml.nodeType!=1||!(result=attr&&((avalue=xml.getAttribute(attr))||typeof avalue=="string")||func&&func(xml)))){xml=xml.parentNode; -}if(avalue==""){return"";}return !result&&attr&&apf.config?apf.config[attr]:result; -};apf.createNodeFromXpath=function(contextNode,xPath,addedNodes,forceNew){var xmlNode,foundpath="",paths=xPath.replace(/('.*?')|(".*?")|\|/g,function(m,m1,m2){if(m1||m2){return m1||m2; -}return"-%-|-%-";}).split("-%-|-%-")[0].split(/\/(?!\/)/);if(!forceNew&&(xmlNode=contextNode.selectSingleNode(xPath))){return xmlNode; -}var len=paths.length-1;if(forceNew){if(paths[len].trim().match(/^\@(.*)$|^text\(\)$/)){len--; -}}if(!paths[0]){contextNode=contextNode.ownerDocument.documentElement;paths.shift(); -paths.shift();len--;len--;}for(var addedNode,isAdding=false,i=0;i1){done[name]=true;for(j=0,l2=sameNodes.length; -j0){for(j=0;j0?"[_]=":"=")+escape(node.firstChild.nodeValue.trim())); -}else{if(attr_len==0){if(includeEmpty){output.push(nm);}}}}tagNames[name]++;}else{if(node.nodeType==3&&isSub){var nval=node.nodeValue; -if(nval&&nval.trim()!==""){output.push(basename+"="+escape(nval));}}}}if(!isSub&&xml.getAttribute("id")){output.push("id="+escape(xml.getAttribute("id"))); -}if(output.length){return output.join("&");}return output;}};apf.convertXml=function(xml,to){return apf.convertMethods[to](xml); -};apf.getTextNode=function(x){for(var i=0,l=x.childNodes.length;i\n/g,">").replace(/\>/g,">\n").replace(/\n\]+[^\/]\>/)?depth++:depth)))+lines[i]; -}if(!strXml){return"";}return lines.join("\n");};apf.xmlToXpath=function(xmlNode,xmlContext,useAID){if(!xmlNode){return""; -}if(useAID===true&&xmlNode.nodeType==1&&xmlNode.getAttribute(apf.xmldb.xmlIdTag)){return"//node()[@"+apf.xmldb.xmlIdTag+"='"+xmlNode.getAttribute(apf.xmldb.xmlIdTag)+"']"; -}if(apf!=this&&this.lookup&&this.select){var unique,def=this.lookup[xmlNode.tagName]; -if(def){unique=xmlNode.selectSingleNode(def).nodeValue;return"//"+xmlNode.tagName+"["+def+"='"+unique+"']"; -}for(var i=0,l=this.select.length;i=0;i--){node=n[i]; -if(selector&&!node.selectSingleNode("self::node()["+selector+"]")){continue;}if(apf.b.$state){apf.b.$queue.push({action:"removeNode",args:[node]}); -}else{if(this.$local){node.parentNode.removeChild(node);}else{apf.xmldb.removeNode(node); -}}}return this;};this.children=function(selector){var nodes=[];for(var node,child,i=0,l=this.$nodes.length; -i=0; -i--){func.call(this.$nodes[i],i);}return this;};this.map=function(callback){var values=[]; -for(var i=0,l=this.$nodes.length;imax){max=curNode[id];count=1;}else{if(curNode[id]==max){count++; -}}}if(!max){count=s.length;for(i=count;i>=0;i--){if(!s[i]){continue;}pNode=(node=s[i]).parentNode; -if(node.$isValid||!include[node.$uniqueId]||!oldInclude[pNode.$uniqueId]){delete include[node.$uniqueId],count--; -continue;}if(!pInclude[pNode.$uniqueId]){pInclude[pNode.$uniqueId]=pNode,pInclude.length++; -}found.push(node);}}else{for(id in curNode){if(curNode[id]!=max){continue;}pNode=(node=apf.all[id]).parentNode; -if(node.$isValid||!include[node.$uniqueId]||!oldInclude[pNode.$uniqueId]){delete include[node.$uniqueId]; -count--;continue;}if(!pInclude[pNode.$uniqueId]){pInclude[pNode.$uniqueId]=pNode,pInclude.length++; -}found.push(node);}}if(found.length){curParentNode.include=pInclude;}for(first=null,i=0,l=found.length; -i1){if(!first){first={node:el,arr:s,curNode:curNode,curParentNode:curParentNode,found:found,childNumber:childNumber}; -(foundEl[curPath]||(foundEl[curPath]=[])).push(first);}if(count>(item.$matchedNodes||0)){item.$matchedNodes=count; -}}else{item.$isValid=true;for(var j=s.length-1;j>=0;j--){if(s[j]==item){delete s[j]; -break;}}}}if(!found.length){var found=[];for(var prop in curNode.include){if(prop=="length"){continue; -}found.push(curNode.include[prop]);}(foundEl[curPath]||(foundEl[curPath]=[])).push({arr:s,node:el,curParentNode:curParentNode,curNode:curNode,found:found,childNumber:childNumber}); -}curNode.curNode=found[0],curNode.parent=curParentNode,curNode.curPath=curPath; -})(doc2,"",hash,doc1,curNode);function checkOrder(node1,node2){if(node1.$childNumber!=node2.childNumber){reorderRules.push([node1,node2.childNumber]); -}}function recurEl(path){var pathCollision=foundEl[path];if(!pathCollision){return; -}var curItem,curMatch,l,count,nMatch,j,i,potentialMatches,leastMatchNodes,pl,pNode; -for(i=0,pl=pathCollision.length;i0 "; + + if (top == self) { + document.body.insertAdjacentHTML("beforeend", + ""); + document.frames["nav"].document.open(); + document.frames["nav"].document.write(str); + document.frames["nav"].document.close(); + } + + this.iframe = document.frames["nav"];// : document.getElementById("nav").contentWindow; + //Check to see if url has been manually changed + this.timer2 = setInterval(function(){ + if (!apf.history.changingHash && location.hash != "#" + apf.history.page) { + var name = location.hash.replace(/^#/, ""); + var page = apf.history.page; + apf.history.setHash(name, true, true); + apf.history.page = page; + apf.history.hasChanged(name); + } + }, apf.history.delay || 1); + } + else { + preInit(); + apf.history.lastUrl = location.href.toString(); + this.timer2 = setInterval(function(){ + if (apf.history.lastUrl == location.href.toString()) + return; + + apf.history.lastUrl = location.href.toString(); + //var page = location.href.replace(/^.*#(.*)$/, "$1") + var page = location.hash.replace("#", "");//.replace(/^.*#(.*)$/,"$1"); + apf.history.hasChanged(decodeURI(page)); + }, 20); + } + }, + to_name : null, + + /** + * Sets the hash value of the location bar in the browser. This is used + * to represent the state of the application for use by the back and forward + * buttons as well as for use when bookmarking or sharing url's. + * @param {String} name the new hash value. + * @param {Boolean} timed whether to add a delay to setting the value. + */ + setHash : function(name, timed, force){ + if (this.changing || this.page == name || !force + && decodeURIComponent(location.hash) == "#" + decodeURIComponent(name)) { + this.to_name = name; + return; + } + + if (!apf.supportHashChange && apf.isIE && !timed) { + this.to_name = name; + return $setTimeout(function(){ + apf.history.setHash(apf.history.to_name, true, force); + }, 200); + } + + this.changePage(name); + if (!this.inited) + return this.init(name); + + if (!apf.supportHashChange && apf.isIE) { + var h = this.iframe.document.body + .appendChild(this.iframe.document.createElement('h1')); + h.id = name; + h.innerHTML = "1"; + } + + (!apf.supportHashChange && apf.isIE ? this.iframe : window).location.href = "#" + name; + + if (!apf.isIE && !apf.isGecko && !apf.isIphone) + apf.history.lastUrl = location.href.toString(); + //else if (apf.isIE8) + }, + + timer : null, + changePage: function(page, force){ + if (!apf.supportHashChange && apf.isIE) { + this.page = page; + this.changingHash = true; + clearTimeout(this.timer); + this.timer = $setTimeout(function(){ + location.hash = page; + apf.history.changingHash = false; + }, 1); + } + }, + + update: function(page) { + var i, l, idx = 0; + + // check past: + for (i = 0, l = this.past.length; i < l && idx === 0; i++) { + if (this.past[i] == page) + idx = i + 1; + } + if (idx > 0) { + // part of past up till page (Array.slice), EXCLUDING page + this.future = this.past.slice(idx, this.past.length - 1) + .concat(this.future).makeUnique(); + this.past.splice(idx, this.past.length - (idx)); + idx = -idx; + } + else { + // check future: + for (i = 0, l = this.future.length; i < l && idx === 0; i++) { + if (this.future[i] == page) { + idx = i + 1; + // current past + part of the future up till page + // (Array.splice), INCLUDING page + this.past = this.past.concat(this.future + .splice(0, this.future.length - idx)).makeUnique(); + } + } + if (idx === 0) { + this.past.push(page); + idx = 1; + } + } + + return idx; + }, + + hasChanged: function(page, force){ + if (page == this.page && !force) + return; + this.changePage(page, force); + + this.changing = true; + if (apf.dispatchEvent("hashchange", { + oldURL : this.page, + newURL : page, + page : page, + index : this.update(page) + }) === false) { + page = location.hash = this.page; + }; + this.changing = false; + + this.page = page; + } +}; + + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/core/lib/config.js)SIZE(8175)TIME(Fri, 14 Oct 2011 08:01:44 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + + +apf.config = new apf.Class().$init(); +apf.extend(apf.config, { + //Defaults + disableRightClick : false, + allowSelect : false, + allowBlur : true, + autoDisableActions : true, + autoDisable : false, /** @todo fix this to only autodisable when createmodel is not true */ + disableF5 : true, + autoHideLoading : true, + disableSpace : true, + defaultPage : "home", + disableBackspace : true, + undokeys : false, + outline : false, + dragOutline : false, + resizeOutline : false, + autoDisableNavKeys : true, + disableTabbing : false, + resourcePath : null, + initdelay : true, + liveText : false, + + + + skinset : "default", + name : apf.isO3 ? "o3App" : self.window && window.location.href.replace(/[^0-9A-Za-z_]/g, "_"), + + tags : {}, + defaults : {}, + baseurl : "", + + "model" : "@default", + "empty-message" : "No items", + "loading-message" : "Loading...", + "offline-message" : "You are currently offline.", + + setDefaults : function(){ + + if (apf.isParsingPartial) { + this.disableRightClick = false; + this.allowSelect = true; + this.autoDisableActions = true; + this.autoDisable = false; + this.disableF5 = false; + this.autoHideLoading = true; + this.disableSpace = false; + this.disableBackspace = false; + this.undokeys = false; + this.disableTabbing = true; + this.allowBlur = true; + } + + }, + + getDefault : function(type, prop){ + var d = this.defaults[type]; + if (!d) + return; + + for (var i = d.length - 1; i >= 0; i--) { + if (d[i][0] == prop) + return d[i][1]; + } + }, + + $handlePropSet : function(name, value){ + //this[name] = value; + //@todo I dont want to go through all the code again, maybe later + this[name.replace(/-(\w)/g, function(m, m1){ + return m1.toUpperCase() + })] = this[name] = value; + + (this.$propHandlers && this.$propHandlers[name] + || apf.GuiElement.propHandlers[name] || apf.K).call(this, value); + }, + + $inheritProperties : {}, + + $propHandlers : { + "baseurl" : function(value){ + this.baseurl = apf.parseExpression(value); + }, + "language" : function(value){ + + }, + "resource-path" : function(value){ + this.resourcePath = apf.parseExpression(value || "") + .replace(/resources\/?|\/$/g, ''); + }, + + + "skinset" : function(value) { + if (this.$amlLoaded) + apf.skins.changeSkinset(value); + }, + + + "outline" : function(value) { + this.dragOutline = + this.resizeOutline = + this.outline = apf.isTrue(value); + }, + "drag-outline" : function(value){ + this.dragOutline = value + ? apf.isTrue(value) + : false; + }, + "resize-outline" : function(value){ + this.resizeOutline = value + ? !apf.isFalse(value) + : false; + }, + + + "login" : function(value, x) { + apf.auth.init(x); + }, + + + + + + "debug" : function(value) { + + apf.debug = value; + } + } +}); + + +if (apf.history) + apf.addEventListener("load", function(){ + apf.history.init(apf.config.defaultPage, "page"); + }); + + + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/core/lib/offline.js)SIZE(20264)TIME(Fri, 14 Oct 2011 08:01:44 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + +apf.offline = { + onLine : true +} + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/core/lib/offline/application.js)SIZE(11733)TIME(Fri, 14 Oct 2011 08:01:44 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/core/lib/offline/gears.js)SIZE(4771)TIME(Fri, 14 Oct 2011 08:01:44 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/core/lib/offline/detector.js)SIZE(4827)TIME(Fri, 14 Oct 2011 08:01:44 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/core/lib/offline/models.js)SIZE(5471)TIME(Fri, 14 Oct 2011 08:01:44 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/core/lib/offline/queue.js)SIZE(7008)TIME(Fri, 14 Oct 2011 08:01:44 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/core/lib/offline/state.js)SIZE(7978)TIME(Fri, 14 Oct 2011 08:01:44 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/core/lib/offline/transactions.js)SIZE(9780)TIME(Fri, 14 Oct 2011 08:01:44 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/core/lib/data.js)SIZE(16420)TIME(Fri, 14 Oct 2011 08:01:44 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + + +/** + * @term datainstruction Data instructions offer a single and consistent way for + * storing and retrieving + * data from different data sources. For instance from a webserver using REST + * or RPC, or from local data sources such as gears, air, o3, html5, as well as + * from in memory sources from javascript or cookies. There is often an xml + * element which is relevant to storing information. This element can be + * accessed using xpath statements in the data instruction using curly braces. + * + * - for complex model expr. replace model. + * - use property binding for selection, instead of setConnections + * + * + * + * + * + * + * + * - create exec function for async objects + * - have list of async objects + * + * Syntax: + * Using data instructions to retrieve data + * + * model="name_of_model" + * model="[name_of_model::xpath]" + * model="{element.selected}" + * model="[local(element.selected) {xpath}]" + * model="{element.choose}" + * model="[local(element.choose) {xpath}]" + * model="[local(element.root) {xpath}]" + * load="[comm.doCall([@id], test(), 5+10).xml]" + * get="example.jsp" + * get="http://www.bla.nl?blah=10&foo=[@bar]&example=[10+5]" + * get="{comm.submit('abc', [@bar])}" + * get="[local(comm.submit('abc', [@bar])) return [xpath]]" + * get="[submit('abc', [@bar])]" + * get="{xmpp.login(username, password)}" + * get="{webdav.getRoot()}" + * get="[10+5]" + * + * + * Syntax: + * Using data instructions to store data + * + * set="http://www.bla.nl?blah=10&foo={/bar}&example=[10+5]" + * set="post http://www.bla.nl?blah=10&foo={/bar}&example=[10+5]" + * + * set="[submit('abc', {/bar})]" + * set="[example=[@name]]" + * set="[apf.setcookie('something', [.])]" + * set="[o3.fs.get('/var/test.xml').data = [.]]" + * + * + * [ + * function test(){ + * var blah = comm.blah(); + * return blah; + * } + * ] + * + * + * See: + *
    + *
  • {@link teleport.cgi the cgi teleport module}
  • + *
  • {@link teleport.rpc the rpc teleport module}
  • + *
  • {@link teleport.webdav the webdav teleport module}
  • + *
  • {@link teleport.xmpp the xmpp teleport module}
  • + *
+ */ + +/** + * Stores data using a {@link term.datainstruction data instruction}. + * + * @param {String} instruction the {@link term.datainstruction data instruction} to be used to store the data. + * @param {Object} [options] the options for this instruction + * Properties: + * {Boolean} multicall whether this call should not be executed immediately but saved for later sending using the purge() command. + * {mixed} userdata any data that is useful to access in the callback function. + * {Array} args the arguments of the call, overriding any specified in the data instruction. + * {XMLElement} [xmlContext] the subject of the xpath queries + * {Function} [callback] the code that is executed when the call returns, either successfully or not. + */ +apf.saveData = + +/** + * Retrieves data using a {@link term.datainstruction data instruction}. + * Example: + * Several uses for a data instruction + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * @param {String} instruction the {@link term.datainstruction data instruction} to be used to retrieve the data. + * @param {XMLElement} [xmlContext] the subject of the xpath queries + * @param {Object} [options] the options for this instruction + * Properties: + * {Boolean} multicall whether this call should not be executed immediately but saved for later sending using the purge() command. + * {mixed} userdata any data that is useful to access in the callback function. + * {mixed} data data to use in the call + * {Array} args the arguments of the call, overriding any specified in the data instruction. + * @param {Function} [callback] the code that is executed when the call returns, either successfully or not. + */ +apf.getData = function(instruction, options){ + if (!instruction) return false; + + //Instruction type detection + var result, chr = instruction.charAt(0), callback = options.callback; + + + var gCallback = + + + function(data, state, extra){ + var callback = options.callback + + if (state != apf.SUCCESS) + return callback(data, state, extra || {}); + + //Change this to warning? + /*if (!data) { + throw new Error(apf.formatErrorString(0, null, + "Loading new data", "Could not load data. \n\ + Data instruction: '" + instruction + "'")); + }*/ + + return callback(data, state, extra || {}); + } + + if (!options) options = {}; //@todo optimize? + var fParsed = options.fParsed || (instruction.indexOf("{") > -1 || instruction.indexOf("[") > -1 + ? apf.lm.compile(instruction, { + withopt : true, + precall : options.precall, + alwayscb : true, + simplexpath : true + }) + : {str: instruction, type: 2}); + + if (options.precall && !options._pc) { + options.asyncs = fParsed.asyncs; + options._pc = 1; + } + + //@todo hack because we're not using compileNode.. an imperfection.. + if (fParsed.type == 3){ + if (fParsed.xpaths[0]) { + var model = fParsed.xpaths[0], xpath = fParsed.xpaths[1]; + + //@todo can this be async? + if (model == "#" || xpath == "#") { //When there is a set model and not a generated xpath + var m = (apf.lm.compile(instruction, { + xpathmode: 5 + }))(); + + //@todo apf3 this needs to be fixed in live markup + if (typeof m != "string") { + model = m.model && m.model.$isModel && m.model; + if (model) + xpath = m.xpath; + else if (m.model) { + model = apf.xmldb.findModel(m.model); + xpath = apf.xmlToXpath(m.model, model.data) + (m.xpath ? "/" + m.xpath : ""); //@todo make this better + } + else { + //Model is not yet available. When it comes available we will be recalled (at least for prop binds) + return; + } + } + else model = null; + } + else { + + model = apf.nameserver.get("model", model) + + } + + + + return gCallback(model.data.selectSingleNode(xpath), apf.SUCCESS, {}); + } + else { + + + return gCallback(options.xmlNode.data.selectSingleNode(fParsed.xpaths[1]), apf.SUCCESS, {}); + } + } + + //xml generation + if (chr == "<") { + //string only + if (fParsed.type == 2) + result = fParsed.str; + else + return fParsed(options.xmlNode, gCallback, options); + } + //jslt fetching data + else if ((chr == "[" || chr == "{")) { //(fParsed.asyncs || fParsed.models) && + return fParsed(options.xmlNode, gCallback, options); + } + //url + else { + if (fParsed.type == 1 || fParsed.type == 3) { + var callback2 = callback; + callback = options.callback = function(data, state, extra){ + if (options._pc === true) + return; + + if (state != apf.SUCCESS) + return callback2.apply(this, arguments); + + var url = data.split(" "), method = "get"; + if (url.length > 1 && url[0].length < 10) { + method = url.shift(); + url = url.join(" "); + } + else url = data; + + callback = options.callback = callback2; + apf.oHttp.exec(method, [url], gCallback, options); + } + fParsed(options.xmlNode, gCallback, options); + } + else { + if (options._pc === true) + return; + + var url = instruction.split(" "), method = "get"; + if (url.length > 1 && url[0].length < 10) { + method = url.shift(); + url = url.join(" "); + } + else { + url = instruction; + } + + apf.oHttp.exec(method, [url.replace(/\\/g, "")], gCallback, options); + } + } + + if (result) { + if (callback) + gCallback(result, apf.SUCCESS, {}); + else { + //apf.console.warn("Returning data directly in apf.getData(). \ + //This means that all callback communication ends in void!"); + return result; + } + } +}; + + +/** + * Creates a model object based on a {@link term.datainstruction data instruction}. + * + * @param {String} instruction the {@link term.datainstruction data instruction} to be used to retrieve the data for the model. + * @param {AmlNode} amlNode the element the model is added to. + */ +apf.setModel = function(instruction, amlNode){ + if (!instruction) return; + + //Find existing model + var fParsed = instruction.indexOf("{") > -1 || instruction.indexOf("[") > -1 + ? apf.lm.compile(instruction, { + //precall : false, + alwayscb : true + }) + : { + type: 2, + str : instruction + }; + + if (instruction == "@default" || fParsed.type == 2) { + + var model = apf.nameserver.get("model", instruction); + if (model) + return model.register(amlNode); + else + + if (instruction == "@default") + return; + + //@todo apf3.0 check here if string is valid url (relative or absolute) + if (instruction.indexOf(".") == -1 && instruction.indexOf("/") == -1) { + + return; + } + } + + //Just an xpath doesnt work. We don't have context + //var l, x; + if (fParsed.type == 3) {//This won't work for complex xpaths + if (fParsed.models) { //check for # in xpaths[i] to determine if its calculated + if (fParsed.xpaths.length == 2 && fParsed.xpaths[0] != '#' && fParsed.xpaths [1] != '#') { + + + + apf.nameserver.get("model", fParsed.xpaths[0]).register(amlNode, fParsed.xpaths[1]); + + return; + } + } + + } + + if (amlNode.clear) + amlNode.clear("loading"); + + //Complex data fetch (possibly async) - data is loaded only once. + //Potential property binding has to take of the rest + apf.getData(instruction, { + parsed : fParsed, + xmlNode : amlNode && amlNode.xmlRoot, + callback : function(data, state, extra){ + //@todo apf3.0 call onerror on amlNode + if (state != apf.SUCCESS) { + throw new Error(apf.formatErrorString(0, null, + "Loading new data", "Could not load data into model. \ + \nMessage: " + extra.message + "\ + \nInstruction: '" + instruction + "'")); + } + + if (!data) + return amlNode.clear && amlNode.clear(); + + if (typeof data == "string") { + if (data.charAt(0) == "<") + data = apf.getXml(data); + else { + //Assuming web service returned url + if (data.indexOf("http://") == 0) + return apf.setModel(data, amlNode); + else { + throw new Error("Invalid data from server");//@todo apf3.0 make proper apf error handling. apf.onerror + } + } + } + + if (data.nodeFunc) { //Assuming a model was passed -- data.localName == "model" && + data.register(amlNode); + return; + } + + var model = apf.xmldb.findModel(data); //See if data is already loaded into a model + if (model) + model.register(amlNode, apf.xmlToXpath(data, model.data)); //@todo move function to xml library + else + new apf.model().register(amlNode).load(data); + }}); +}; + + + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/core/lib/date.js)SIZE(40737)TIME(Fri, 14 Oct 2011 08:01:44 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + +/** + * @version: 1.0 Alpha-1 + * @author: Coolite Inc. http://www.coolite.com/ + * @date: 2008-04-13 + * @copyright: Copyright (c) 2006-2008, Coolite Inc. (http://www.coolite.com/). All rights reserved. + * @license: Licensed under The MIT License. See license.txt and http://www.datejs.com/license/. + * @website: http://www.datejs.com/ + */ + +(function () { + var $C = { + /* Culture Name */ + name: "en-US", + englishName: "English (United States)", + nativeName: "English (United States)", + + /* Day Name Strings */ + dayNames: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"], + abbreviatedDayNames: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"], + shortestDayNames: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"], + firstLetterDayNames: ["S", "M", "T", "W", "T", "F", "S"], + + /* Month Name Strings */ + monthNames: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"], + abbreviatedMonthNames: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"], + + /* AM/PM Designators */ + amDesignator: "AM", + pmDesignator: "PM", + + firstDayOfWeek: 0, + twoDigitYearMax: 2029, + + /** + * The dateElementOrder is based on the order of the + * format specifiers in the formatPatterns.DatePattern. + * + * Example: +
+         shortDatePattern    dateElementOrder
+         ------------------  ---------------- 
+         "M/d/yyyy"          "mdy"
+         "dd/MM/yyyy"        "dmy"
+         "yyyy-MM-dd"        "ymd"
+         
+ * + * The correct dateElementOrder is required by the parser to + * determine the expected order of the date elements in the + * string being parsed. + */ + dateElementOrder: "mdy", + + /* Standard date and time format patterns */ + formatPatterns: { + shortDate: "M/d/yyyy", + longDate: "dddd, MMMM dd, yyyy", + shortTime: "h:mm tt", + longTime: "h:mm:ss tt", + fullDateTime: "dddd, MMMM dd, yyyy h:mm:ss tt", + sortableDateTime: "yyyy-MM-ddTHH:mm:ss", + universalSortableDateTime: "yyyy-MM-dd HH:mm:ssZ", + rfc1123: "ddd, dd MMM yyyy HH:mm:ss GMT", + monthDay: "MMMM dd", + yearMonth: "MMMM, yyyy" + }, + + /** + * NOTE: If a string format is not parsing correctly, but + * you would expect it parse, the problem likely lies below. + * + * The following regex patterns control most of the string matching + * within the parser. + * + * The Month name and Day name patterns were automatically generated + * and in general should be (mostly) correct. + * + * Beyond the month and day name patterns are natural language strings. + * Example: "next", "today", "months" + * + * These natural language string may NOT be correct for this culture. + * If they are not correct, please translate and edit this file + * providing the correct regular expression pattern. + * + * If you modify this file, please post your revised CultureInfo file + * to the Datejs Forum located at http://www.datejs.com/forums/. + * + * Please mark the subject of the post with [CultureInfo]. Example: + * Subject: [CultureInfo] Translated "da-DK" Danish(Denmark) + * + * We will add the modified patterns to the master source files. + * + * As well, please review the list of "Future Strings" section below. + */ + regexPatterns: { + jan: /^jan(uary)?/i, + feb: /^feb(ruary)?/i, + mar: /^mar(ch)?/i, + apr: /^apr(il)?/i, + may: /^may/i, + jun: /^jun(e)?/i, + jul: /^jul(y)?/i, + aug: /^aug(ust)?/i, + sep: /^sep(t(ember)?)?/i, + oct: /^oct(ober)?/i, + nov: /^nov(ember)?/i, + dec: /^dec(ember)?/i, + + sun: /^su(n(day)?)?/i, + mon: /^mo(n(day)?)?/i, + tue: /^tu(e(s(day)?)?)?/i, + wed: /^we(d(nesday)?)?/i, + thu: /^th(u(r(s(day)?)?)?)?/i, + fri: /^fr(i(day)?)?/i, + sat: /^sa(t(urday)?)?/i, + + future: /^next/i, + past: /^last|past|prev(ious)?/i, + add: /^(\+|aft(er)?|from|hence)/i, + subtract: /^(\-|bef(ore)?|ago)/i, + + yesterday: /^yes(terday)?/i, + today: /^t(od(ay)?)?/i, + tomorrow: /^tom(orrow)?/i, + now: /^n(ow)?/i, + + millisecond: /^ms|milli(second)?s?/i, + second: /^sec(ond)?s?/i, + minute: /^mn|min(ute)?s?/i, + hour: /^h(our)?s?/i, + week: /^w(eek)?s?/i, + month: /^m(onth)?s?/i, + day: /^d(ay)?s?/i, + year: /^y(ear)?s?/i, + + shortMeridian: /^(a|p)/i, + longMeridian: /^(a\.?m?\.?|p\.?m?\.?)/i, + timezone: /^((e(s|d)t|c(s|d)t|m(s|d)t|p(s|d)t)|((gmt)?\s*(\+|\-)\s*\d\d\d\d?)|gmt|utc)/i, + ordinalSuffix: /^\s*(st|nd|rd|th)/i, + timeContext: /^\s*(\:|a(?!u|p)|p)/i + }, + + timezones: [ + {name:"UTC", offset:"-000"}, + {name:"GMT", offset:"-000"}, + {name:"EST", offset:"-0500"}, + {name:"EDT", offset:"-0400"}, + {name:"CST", offset:"-0600"}, + {name:"CDT", offset:"-0500"}, + {name:"MST", offset:"-0700"}, + {name:"MDT", offset:"-0600"}, + {name:"PST", offset:"-0800"}, + {name:"PDT", offset:"-0700"} + ] + }; + + var $D = Date, + $P = $D.prototype, + p = function(s, l) { + if (!l) + l = 2; + return ("000" + s).slice(l * -1); + }; + + /** + * Resets the time of this Date object to 12:00 AM (00:00), which is the + * start of the day. + * @param {Boolean} .clone() this date instance before clearing Time + * @return {Date} this + */ + $P.clearTime = function() { + this.setHours(0); + this.setMinutes(0); + this.setSeconds(0); + this.setMilliseconds(0); + return this; + }; + + /** + * Resets the time of this Date object to the current time ('now'). + * @return {Date} this + */ + $P.setTimeToNow = function() { + var n = new Date(); + this.setHours(n.getHours()); + this.setMinutes(n.getMinutes()); + this.setSeconds(n.getSeconds()); + this.setMilliseconds(n.getMilliseconds()); + return this; + }; + + /** + * Gets a date that is set to the current date. The time is set to the start + * of the day (00:00 or 12:00 AM). + * @return {Date} The current date. + */ + $D.today = function() { + return new Date().clearTime(); + }; + + /** + * Compares the first date to the second date and returns an number indication + * of their relative values. + * @param {Date} First Date object to compare [Required]. + * @param {Date} Second Date object to compare to [Required]. + * @return {Number} -1 = date1 is lessthan date2. 0 = values are equal. + * 1 = date1 is greaterthan date2. + */ + $D.compare = function(date1, date2) { + if (isNaN(date1) || isNaN(date2)) + throw new Error(date1 + " - " + date2); + else if (date1 instanceof Date && date2 instanceof Date) + return (date1 < date2) ? -1 : (date1 > date2) ? 1 : 0; + else + throw new TypeError(date1 + " - " + date2); + }; + + /** + * Compares the first Date object to the second Date object and returns true + * if they are equal. + * @param {Date} First Date object to compare [Required] + * @param {Date} Second Date object to compare to [Required] + * @return {Boolean} true if dates are equal. false if they are not equal. + */ + $D.equals = function(date1, date2) { + return (date1.compareTo(date2) === 0); + }; + + /** + * Gets the day number (0-6) if given a CultureInfo specific string which is + * a valid dayName, abbreviatedDayName or shortestDayName (two char). + * @param {String} The name of the day (eg. "Monday, "Mon", "tuesday", "tue", "We", "we"). + * @return {Number} The day number + */ + $D.getDayNumberFromName = function(name) { + var n = $C.dayNames, + m = $C.abbreviatedDayNames, + o = $C.shortestDayNames, + s = name.toLowerCase(); + for (var i = 0; i < n.length; i++) { + if (n[i].toLowerCase() == s || m[i].toLowerCase() == s || o[i].toLowerCase() == s) + return i; + } + return -1; + }; + + /** + * Gets the month number (0-11) if given a Culture Info specific string which + * is a valid monthName or abbreviatedMonthName. + * @param {String} The name of the month (eg. "February, "Feb", "october", "oct"). + * @return {Number} The day number + */ + $D.getMonthNumberFromName = function(name) { + var n = $C.monthNames, + m = $C.abbreviatedMonthNames, + s = name.toLowerCase(); + for (var i = 0; i < n.length; i++) { + if (n[i].toLowerCase() == s || m[i].toLowerCase() == s) + return i; + } + return -1; + }; + + /** + * Determines if the current date instance is within a LeapYear. + * @param {Number} The year. + * @return {Boolean} true if date is within a LeapYear, otherwise false. + */ + $D.isLeapYear = function(year) { + return ((year % 4 === 0 && year % 100 !== 0) || year % 400 === 0); + }; + + /** + * Gets the number of days in the month, given a year and month value. + * Automatically corrects for LeapYear. + * @param {Number} The year. + * @param {Number} The month (0-11). + * @return {Number} The number of days in the month. + */ + $D.getDaysInMonth = function(year, month) { + return [31, ($D.isLeapYear(year) ? 29 : 28), 31, 30, 31, 30, 31, 31, 30, 31, 30, 31][month]; + }; + + $D.getTimezoneAbbreviation = function(offset) { + var z = $C.timezones, p; + for (var i = 0; i < z.length; i++) { + if (z[i].offset === offset) + return z[i].name; + } + return null; + }; + + $D.getTimezoneOffset = function(name) { + var z = $C.timezones, p; + for (var i = 0; i < z.length; i++) { + if (z[i].name === name.toUpperCase()) + return z[i].offset; + } + return null; + }; + + /** + * Returns a new Date object that is an exact date and time copy of the + * original instance. + * @return {Date} A new Date instance + */ + $P.clone = function() { + return new Date(this.getTime()); + }; + + /** + * Compares this instance to a Date object and returns an number indication + * of their relative values. + * @param {Date} Date object to compare [Required] + * @return {Number} -1 = this is lessthan date. 0 = values are equal. + * 1 = this is greaterthan date. + */ + $P.compareTo = function(date) { + return Date.compare(this, date); + }; + + /** + * Compares this instance to another Date object and returns true if they are equal. + * @param {Date} Date object to compare. If no date to compare, new Date() + * [now] is used. + * @return {Boolean} true if dates are equal. false if they are not equal. + */ + $P.equals = function(date) { + return Date.equals(this, date || new Date()); + }; + + /** + * Determines if this instance is between a range of two dates or equal to + * either the start or end dates. + * @param {Date} Start of range [Required] + * @param {Date} End of range [Required] + * @return {Boolean} true is this is between or equal to the start and end + * dates, else false + */ + $P.between = function(start, end) { + return this.getTime() >= start.getTime() && this.getTime() <= end.getTime(); + }; + + /** + * Determines if this date occurs after the date to compare to. + * @param {Date} Date object to compare. If no date to compare, new Date() + * ("now") is used. + * @return {Boolean} true if this date instance is greater than the date to + * compare to (or "now"), otherwise false. + */ + $P.isAfter = function(date) { + return this.compareTo(date || new Date()) === 1; + }; + + /** + * Determines if this date occurs before the date to compare to. + * @param {Date} Date object to compare. If no date to compare, new Date() + * ("now") is used. + * @return {Boolean} true if this date instance is less than the date to + * compare to (or "now"). + */ + $P.isBefore = function(date) { + return (this.compareTo(date || new Date()) === -1); + }; + + /** + * Determines if the current Date instance occurs today. + * @return {Boolean} true if this date instance is 'today', otherwise false. + */ + + /** + * Determines if the current Date instance occurs on the same Date as the supplied 'date'. + * If no 'date' to compare to is provided, the current Date instance is compared to 'today'. + * @param {date} Date object to compare. If no date to compare, the current Date ("now") is used. + * @return {Boolean} true if this Date instance occurs on the same Day as the supplied 'date'. + */ + $P.isToday = $P.isSameDay = function(date) { + return this.clone().clearTime().equals((date || new Date()).clone().clearTime()); + }; + + /** + * Adds the specified number of milliseconds to this instance. + * @param {Number} The number of milliseconds to add. The number can be + * positive or negative [Required] + * @return {Date} this + */ + $P.addMilliseconds = function(value) { + this.setMilliseconds(this.getMilliseconds() + value * 1); + return this; + }; + + /** + * Adds the specified number of seconds to this instance. + * @param {Number} The number of seconds to add. The number can be positive + * or negative [Required] + * @return {Date} this + */ + $P.addSeconds = function(value) { + return this.addMilliseconds(value * 1000); + }; + + /** + * Adds the specified number of seconds to this instance. + * @param {Number} The number of seconds to add. The number can be positive + * or negative [Required] + * @return {Date} this + */ + $P.addMinutes = function(value) { + return this.addMilliseconds(value * 60000); /* 60*1000 */ + }; + + /** + * Adds the specified number of hours to this instance. + * @param {Number} The number of hours to add. The number can be positive + * or negative [Required] + * @return {Date} this + */ + $P.addHours = function(value) { + return this.addMilliseconds(value * 3600000); /* 60*60*1000 */ + }; + + /** + * Adds the specified number of days to this instance. + * @param {Number} The number of days to add. The number can be positive + * or negative [Required] + * @return {Date} this + */ + $P.addDays = function(value) { + this.setDate(this.getDate() + value * 1); + return this; + }; + + /** + * Adds the specified number of weeks to this instance. + * @param {Number} The number of weeks to add. The number can be positive + * or negative [Required] + * @return {Date} this + */ + $P.addWeeks = function(value) { + return this.addDays(value * 7); + }; + + /** + * Adds the specified number of months to this instance. + * @param {Number} The number of months to add. The number can be positive + * or negative [Required] + * @return {Date} this + */ + $P.addMonths = function(value) { + var n = this.getDate(); + this.setDate(1); + this.setMonth(this.getMonth() + value * 1); + this.setDate(Math.min(n, $D.getDaysInMonth(this.getFullYear(), this.getMonth()))); + return this; + }; + + /** + * Adds the specified number of years to this instance. + * @param {Number} The number of years to add. The number can be positive + * or negative [Required] + * @return {Date} this + */ + $P.addYears = function(value) { + return this.addMonths(value * 12); + }; + + /** + * Adds (or subtracts) to the value of the years, months, weeks, days, hours, + * minutes, seconds, milliseconds of the date instance using given configuration + * object. Positive and Negative values allowed. + * Example +

+    Date.today().add( { days: 1, months: 1 } )
+     
+    new Date().add( { years: -1 } )
+    
+ * @param {Object} Configuration object containing attributes (months, days, etc.) + * @return {Date} this + */ + $P.add = function(config) { + if (typeof config == "number") { + this._orient = config; + return this; + } + + var x = config; + + if (x.milliseconds) + this.addMilliseconds(x.milliseconds); + if (x.seconds) + this.addSeconds(x.seconds); + if (x.minutes) + this.addMinutes(x.minutes); + if (x.hours) + this.addHours(x.hours); + if (x.weeks) + this.addWeeks(x.weeks); + if (x.months) + this.addMonths(x.months); + if (x.years) + this.addYears(x.years); + if (x.days) + this.addDays(x.days); + return this; + }; + + var $y, $m, $d; + + /** + * Get the week number. Week one (1) is the week which contains the first + * Thursday of the year. Monday is considered the first day of the week. + * This algorithm is a JavaScript port of the work presented by Claus + * Tøndering at http://www.tondering.dk/claus/cal/node8.html#SECTION00880000000000000000 + * .getWeek() Algorithm Copyright (c) 2008 Claus Tondering. + * The .getWeek() function does NOT convert the date to UTC. The local datetime + * is used. Please use .getISOWeek() to get the week of the UTC converted date. + * @return {Number} 1 to 53 + */ + $P.getWeek = function() { + var a, b, c, d, e, f, g, n, s, w; + + $y = (!$y) ? this.getFullYear() : $y; + $m = (!$m) ? this.getMonth() + 1 : $m; + $d = (!$d) ? this.getDate() : $d; + + if ($m <= 2) { + a = $y - 1; + b = (a / 4 | 0) - (a / 100 | 0) + (a / 400 | 0); + c = ((a - 1) / 4 | 0) - ((a - 1) / 100 | 0) + ((a - 1) / 400 | 0); + s = b - c; + e = 0; + f = $d - 1 + (31 * ($m - 1)); + } else { + a = $y; + b = (a / 4 | 0) - (a / 100 | 0) + (a / 400 | 0); + c = ((a - 1) / 4 | 0) - ((a - 1) / 100 | 0) + ((a - 1) / 400 | 0); + s = b - c; + e = s + 1; + f = $d + ((153 * ($m - 3) + 2) / 5) + 58 + s; + } + + g = (a + b) % 7; + d = (f + g - e) % 7; + n = (f + 3 - d) | 0; + + if (n < 0) { + w = 53 - ((g - s) / 5 | 0); + } else if (n > 364 + s) { + w = 1; + } else { + w = (n / 7 | 0) + 1; + } + + $y = $m = $d = null; + + return w; + }; + + /** + * Get the ISO 8601 week number. Week one ("01") is the week which contains the + * first Thursday of the year. Monday is considered the first day of the week. + * The .getISOWeek() function does convert the date to it's UTC value. + * Please use .getWeek() to get the week of the local date. + * @return {String} "01" to "53" + */ + $P.getISOWeek = function() { + $y = this.getUTCFullYear(); + $m = this.getUTCMonth() + 1; + $d = this.getUTCDate(); + return p(this.getWeek()); + }; + + /** + * Moves the date to Monday of the week set. Week one (1) is the week which + * contains the first Thursday of the year. + * @param {Number} A Number (1 to 53) that represents the week of the year. + * @return {Date} this + */ + $P.setWeek = function(n) { + return this.moveToDayOfWeek(1).addWeeks(n - this.getWeek()); + }; + + // private + var validate = function(n, min, max, name) { + if (typeof n == "undefined") + return false; + else if (typeof n != "number") + throw new TypeError(n + " is not a Number."); + else if (n < min || n > max) + throw new RangeError(n + " is not a valid value for " + name + "."); + return true; + }; + + /** + * Validates the number is within an acceptable range for milliseconds [0-999]. + * @param {Number} The number to check if within range. + * @return {Boolean} true if within range, otherwise false. + */ + $D.validateMillisecond = function(value) { + return validate(value, 0, 999, "millisecond"); + }; + + /** + * Validates the number is within an acceptable range for seconds [0-59]. + * @param {Number} The number to check if within range. + * @return {Boolean} true if within range, otherwise false. + */ + $D.validateSecond = function(value) { + return validate(value, 0, 59, "second"); + }; + + /** + * Validates the number is within an acceptable range for minutes [0-59]. + * @param {Number} The number to check if within range. + * @return {Boolean} true if within range, otherwise false. + */ + $D.validateMinute = function(value) { + return validate(value, 0, 59, "minute"); + }; + + /** + * Validates the number is within an acceptable range for hours [0-23]. + * @param {Number} The number to check if within range. + * @return {Boolean} true if within range, otherwise false. + */ + $D.validateHour = function(value) { + return validate(value, 0, 23, "hour"); + }; + + /** + * Validates the number is within an acceptable range for the days in a month + * [0 - MaxDaysInMonth]. + * @param {Number} The number to check if within range. + * @return {Boolean} true if within range, otherwise false. + */ + $D.validateDay = function(value, year, month) { + return validate(value, 1, $D.getDaysInMonth(year, month), "day"); + }; + + /** + * Validates the number is within an acceptable range for months [0-11]. + * @param {Number} The number to check if within range. + * @return {Boolean} true if within range, otherwise false. + */ + $D.validateMonth = function(value) { + return validate(value, 0, 11, "month"); + }; + + /** + * Validates the number is within an acceptable range for years. + * @param {Number} The number to check if within range. + * @return {Boolean} true if within range, otherwise false. + */ + $D.validateYear = function(value) { + return validate(value, 0, 9999, "year"); + }; + + /** + * Set the value of year, month, day, hour, minute, second, millisecond of + * date instance using given configuration object. + * Example +

+    Date.today().set( { day: 20, month: 1 } )
+
+    new Date().set( { millisecond: 0 } )
+    
+ * + * @param {Object} Configuration object containing attributes (month, day, etc.) + * @return {Date} this + */ + $P.set = function(config) { + if ($D.validateMillisecond(config.millisecond)) + this.addMilliseconds(config.millisecond - this.getMilliseconds()); + + if ($D.validateSecond(config.second)) + this.addSeconds(config.second - this.getSeconds()); + + if ($D.validateMinute(config.minute)) + this.addMinutes(config.minute - this.getMinutes()); + + if ($D.validateHour(config.hour)) + this.addHours(config.hour - this.getHours()); + + if ($D.validateMonth(config.month)) + this.addMonths(config.month - this.getMonth()); + + if ($D.validateYear(config.year)) + this.addYears(config.year - this.getFullYear()); + + /* day has to go last because you can't validate the day without first knowing the month */ + if ($D.validateDay(config.day, this.getFullYear(), this.getMonth())) + this.addDays(config.day - this.getDate()); + + if (config.timezone) + this.setTimezone(config.timezone); + + if (config.timezoneOffset) + this.setTimezoneOffset(config.timezoneOffset); + + if (config.week && validate(config.week, 0, 53, "week")) + this.setWeek(config.week); + + return this; + }; + + /** + * Moves the date to the first day of the month. + * @return {Date} this + */ + $P.moveToFirstDayOfMonth = function() { + return this.set({ day: 1 }); + }; + + /** + * Moves the date to the last day of the month. + * @return {Date} this + */ + $P.moveToLastDayOfMonth = function() { + return this.set({ day: $D.getDaysInMonth(this.getFullYear(), this.getMonth())}); + }; + + /** + * Moves the date to the next n'th occurrence of the dayOfWeek starting from + * the beginning of the month. The number (-1) is a magic number and will return + * the last occurrence of the dayOfWeek in the month. + * @param {Number} The dayOfWeek to move to + * @param {Number} The n'th occurrence to move to. Use (-1) to return the + * last occurrence in the month + * @return {Date} this + */ + $P.moveToNthOccurrence = function(dayOfWeek, occurrence) { + var shift = 0; + if (occurrence > 0) { + shift = occurrence - 1; + } + else if (occurrence === -1) { + this.moveToLastDayOfMonth(); + if (this.getDay() !== dayOfWeek) + this.moveToDayOfWeek(dayOfWeek, -1); + return this; + } + return this.moveToFirstDayOfMonth().addDays(-1) + .moveToDayOfWeek(dayOfWeek, +1).addWeeks(shift); + }; + + /** + * Move to the next or last dayOfWeek based on the orient value. + * @param {Number} The dayOfWeek to move to + * @param {Number} Forward (+1) or Back (-1). Defaults to +1. [Optional] + * @return {Date} this + */ + $P.moveToDayOfWeek = function(dayOfWeek, orient) { + var diff = (dayOfWeek - this.getDay() + 7 * (orient || +1)) % 7; + return this.addDays((diff === 0) ? diff += 7 * (orient || +1) : diff); + }; + + /** + * Move to the next or last month based on the orient value. + * @param {Number} The month to move to. 0 = January, 11 = December + * @param {Number} Forward (+1) or Back (-1). Defaults to +1. [Optional] + * @return {Date} this + */ + $P.moveToMonth = function(month, orient) { + var diff = (month - this.getMonth() + 12 * (orient || +1)) % 12; + return this.addMonths((diff === 0) ? diff += 12 * (orient || +1) : diff); + }; + + /** + * Get the Ordinal day (numeric day number) of the year, adjusted for leap year. + * @return {Number} 1 through 365 (366 in leap years) + */ + $P.getOrdinalNumber = function() { + return Math.ceil((this.clone().clearTime() + - new Date(this.getFullYear(), 0, 1)) / 86400000) + 1; + }; + + /** + * Get the time zone abbreviation of the current date. + * @return {String} The abbreviated time zone name (e.g. "EST") + */ + $P.getTimezone = function() { + return $D.getTimezoneAbbreviation(this.getUTCOffset()); + }; + + $P.setTimezoneOffset = function(offset) { + var here = this.getTimezoneOffset(), there = Number(offset) * -6 / 10; + return this.addMinutes(there - here); + }; + + $P.setTimezone = function(offset) { + return this.setTimezoneOffset($D.getTimezoneOffset(offset)); + }; + + /** + * Indicates whether Daylight Saving Time is observed in the current time zone. + * @return {Boolean} true|false + */ + $P.hasDaylightSavingTime = function() { + return (Date.today().set({month: 0, day: 1}).getTimezoneOffset() + !== Date.today().set({month: 6, day: 1}).getTimezoneOffset()); + }; + + /** + * Indicates whether this Date instance is within the Daylight Saving Time + * range for the current time zone. + * @return {Boolean} true|false + */ + $P.isDaylightSavingTime = function() { + return Date.today().set({month: 0, day: 1}).getTimezoneOffset() != this.getTimezoneOffset(); + }; + + /** + * Get the offset from UTC of the current date. + * @return {String} The 4-character offset string prefixed with + or - (e.g. "-0500") + */ + $P.getUTCOffset = function() { + var n = this.getTimezoneOffset() * -10 / 6, r; + if (n < 0) { + r = (n - 10000).toString(); + return r.charAt(0) + r.substr(2); + } + else { + r = (n + 10000).toString(); + return "+" + r.substr(1); + } + }; + + $P.getUTCTime = function() { + //Date.UTC(year, month[, date[, hrs[, min[, sec[, ms]]]]]) + return Date.UTC(this.getUTCFullYear(), this.getUTCMonth(), this.getUTCDate(), + this.getUTCHours(), this.getUTCMinutes(), this.getUTCSeconds(), + this.getUTCMilliseconds()); + }; + + /** + * Returns the number of milliseconds between this date and date. + * @param {Date} Defaults to now + * @return {Number} The diff in milliseconds + */ + $P.getElapsed = function(date) { + return (date || new Date()) - this; + }; + + if (!$P.toISOString) { + /** + * Converts the current date instance into a string with an ISO 8601 format. + * The date is converted to it's UTC value. + * @return {String} ISO 8601 string of date + */ + $P.toISOString = function() { + // From http://www.json.org/json.js. Public Domain. + function f(n) { + return n < 10 ? '0' + n : n; + } + + return '"' + this.getUTCFullYear() + '-' + + f(this.getUTCMonth() + 1) + '-' + + f(this.getUTCDate()) + 'T' + + f(this.getUTCHours()) + ':' + + f(this.getUTCMinutes()) + ':' + + f(this.getUTCSeconds()) + 'Z"'; + }; + } + + // private + $P._toString = $P.toString; + + /** + * Converts the value of the current Date object to its equivalent string representation. + * Format Specifiers +
+    CUSTOM DATE AND TIME FORMAT STRINGS
+    Format  Description                                                                  Example
+    ------  ---------------------------------------------------------------------------  -----------------------
+     s      The seconds of the minute between 0-59.                                      "0" to "59"
+     ss     The seconds of the minute with leading zero if required.                     "00" to "59"
+     
+     m      The minute of the hour between 0-59.                                         "0"  or "59"
+     mm     The minute of the hour with leading zero if required.                        "00" or "59"
+     
+     h      The hour of the day between 1-12.                                            "1"  to "12"
+     hh     The hour of the day with leading zero if required.                           "01" to "12"
+     
+     H      The hour of the day between 0-23.                                            "0"  to "23"
+     HH     The hour of the day with leading zero if required.                           "00" to "23"
+     
+     d      The day of the month between 1 and 31.                                       "1"  to "31"
+     dd     The day of the month with leading zero if required.                          "01" to "31"
+     ddd    Abbreviated day name. $C.abbreviatedDayNames.                                "Mon" to "Sun" 
+     dddd   The full day name. $C.dayNames.                                              "Monday" to "Sunday"
+     
+     M      The month of the year between 1-12.                                          "1" to "12"
+     MM     The month of the year with leading zero if required.                         "01" to "12"
+     MMM    Abbreviated month name. $C.abbreviatedMonthNames.                            "Jan" to "Dec"
+     MMMM   The full month name. $C.monthNames.                                          "January" to "December"
+
+     yy     The year as a two-digit number.                                              "99" or "08"
+     yyyy   The full four digit year.                                                    "1999" or "2008"
+     
+     t      Displays the first character of the A.M./P.M. designator.                    "A" or "P"
+            $C.amDesignator or $C.pmDesignator
+     tt     Displays the A.M./P.M. designator.                                           "AM" or "PM"
+            $C.amDesignator or $C.pmDesignator
+     
+     S      The ordinal suffix ("st, "nd", "rd" or "th") of the current day.            "st, "nd", "rd" or "th"
+
+|| *Format* || *Description* || *Example* ||
+|| d      || The CultureInfo shortDate Format Pattern                                     || "M/d/yyyy" ||
+|| D      || The CultureInfo longDate Format Pattern                                      || "dddd, MMMM dd, yyyy" ||
+|| F      || The CultureInfo fullDateTime Format Pattern                                  || "dddd, MMMM dd, yyyy h:mm:ss tt" ||
+|| m      || The CultureInfo monthDay Format Pattern                                      || "MMMM dd" ||
+|| r      || The CultureInfo rfc1123 Format Pattern                                       || "ddd, dd MMM yyyy HH:mm:ss GMT" ||
+|| s      || The CultureInfo sortableDateTime Format Pattern                              || "yyyy-MM-ddTHH:mm:ss" ||
+|| t      || The CultureInfo shortTime Format Pattern                                     || "h:mm tt" ||
+|| T      || The CultureInfo longTime Format Pattern                                      || "h:mm:ss tt" ||
+|| u      || The CultureInfo universalSortableDateTime Format Pattern                     || "yyyy-MM-dd HH:mm:ssZ" ||
+|| y      || The CultureInfo yearMonth Format Pattern                                     || "MMMM, yyyy" ||
+     
+
+    STANDARD DATE AND TIME FORMAT STRINGS
+    Format  Description                                                                  Example ("en-US")
+    ------  ---------------------------------------------------------------------------  -----------------------
+     d      The CultureInfo shortDate Format Pattern                                     "M/d/yyyy"
+     D      The CultureInfo longDate Format Pattern                                      "dddd, MMMM dd, yyyy"
+     F      The CultureInfo fullDateTime Format Pattern                                  "dddd, MMMM dd, yyyy h:mm:ss tt"
+     m      The CultureInfo monthDay Format Pattern                                      "MMMM dd"
+     r      The CultureInfo rfc1123 Format Pattern                                       "ddd, dd MMM yyyy HH:mm:ss GMT"
+     s      The CultureInfo sortableDateTime Format Pattern                              "yyyy-MM-ddTHH:mm:ss"
+     t      The CultureInfo shortTime Format Pattern                                     "h:mm tt"
+     T      The CultureInfo longTime Format Pattern                                      "h:mm:ss tt"
+     u      The CultureInfo universalSortableDateTime Format Pattern                     "yyyy-MM-dd HH:mm:ssZ"
+     y      The CultureInfo yearMonth Format Pattern                                     "MMMM, yyyy"
+    
+ * @param {String} A format string consisting of one or more format spcifiers [Optional]. + * @return {String} A string representation of the current Date object. + */ + $P.toString = function(format) { + var x = this; + + // Standard Date and Time Format Strings. Formats pulled from CultureInfo file and + // may vary by culture. + if (format && format.length == 1) { + var c = $C.formatPatterns; + x.t = x.toString; + switch (format) { + case "d": + return x.t(c.shortDate); + case "D": + return x.t(c.longDate); + case "F": + return x.t(c.fullDateTime); + case "m": + return x.t(c.monthDay); + case "r": + return x.t(c.rfc1123); + case "s": + return x.t(c.sortableDateTime); + case "t": + return x.t(c.shortTime); + case "T": + return x.t(c.longTime); + case "u": + return x.t(c.universalSortableDateTime); + case "y": + return x.t(c.yearMonth); + } + } + + var ord = function (n) { + switch (n * 1) { + case 1: + case 21: + case 31: + return "st"; + case 2: + case 22: + return "nd"; + case 3: + case 23: + return "rd"; + default: + return "th"; + } + }; + + return format ? format.replace(/(\\)?(dd?d?d?|MM?M?M?|yy?y?y?|hh?|HH?|mm?|ss?|tt?|S)/g, + function (m) { + if (m.charAt(0) === "\\") { + return m.replace("\\", ""); + } + x.h = x.getHours; + switch (m) { + case "hh": + return p(x.h() < 13 ? (x.h() === 0 ? 12 : x.h()) : (x.h() - 12)); + case "h": + return x.h() < 13 ? (x.h() === 0 ? 12 : x.h()) : (x.h() - 12); + case "HH": + return p(x.h()); + case "H": + return x.h(); + case "mm": + return p(x.getMinutes()); + case "m": + return x.getMinutes(); + case "ss": + return p(x.getSeconds()); + case "s": + return x.getSeconds(); + case "yyyy": + return p(x.getFullYear(), 4); + case "yy": + return p(x.getFullYear()); + case "dddd": + return $C.dayNames[x.getDay()]; + case "ddd": + return $C.abbreviatedDayNames[x.getDay()]; + case "dd": + return p(x.getDate()); + case "d": + return x.getDate(); + case "MMMM": + return $C.monthNames[x.getMonth()]; + case "MMM": + return $C.abbreviatedMonthNames[x.getMonth()]; + case "MM": + return p((x.getMonth() + 1)); + case "M": + return x.getMonth() + 1; + case "t": + return x.h() < 12 ? $C.amDesignator.substring(0, 1) : $C.pmDesignator.substring(0, 1); + case "tt": + return x.h() < 12 ? $C.amDesignator : $C.pmDesignator; + case "S": + return ord(x.getDate()); + default: + return m; + } + } + ) : this._toString(); + }; +}()); + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/core/lib/draw.js)SIZE(66997)TIME(Fri, 14 Oct 2011 08:01:44 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/core/lib/flow.js)SIZE(71086)TIME(Fri, 14 Oct 2011 08:01:44 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/core/lib/flow2.js)SIZE(70664)TIME(Fri, 14 Oct 2011 08:01:44 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/core/lib/geolocation.js)SIZE(11303)TIME(Fri, 14 Oct 2011 08:01:44 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: socket://www.fsf.org. + * + */ + + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/core/lib/html.js)SIZE(15348)TIME(Fri, 14 Oct 2011 08:01:44 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + +/** + * The parser of the HyperText Markup Language. + * @private + */ +apf.htmlCleaner = (function() { + var prepareRE = null, exportRE = null, + noMarginTags = {"table": 1, "TABLE": 1}, + selfClosing = {"br": 1, "img": 1, "input": 1, "hr": 1}; + + return { + /** + * Processes, sanitizes and cleanses a string of raw html that originates + * from outside a contentEditable area, so that the inner workings of the + * editor are less likely to be affected. + * + * @param {String} html + * @return The sanitized string, valid to store and use in the editor + * @type {String} + */ + prepare: function(html, bNoEnclosing) { + if (!prepareRE) { + // compile 'em regezz + prepareRE = [ + /<(\/?)strong>|]+)>/gi, + /<(\/?)em>|]+)>/gi, + /'/g, + /* + Ruben: due to a bug in IE and FF this regexp won't fly: + /((?:[^<]*|<(?:span|strong|em|u|i|b)[^<]*))]*?>/gi, //@todo Ruben: add here more inline html tag names + */ + /(<(\/?)(span|strong|em|u|i|b|a|strike|sup|sub|font|img)(?:\s+[\s\S]*?)?>)|()|(<(\/?)([\w\-]+)(?:\s+[\s\S]*?)?>)|([^<>]*)/gi, //expensive work around + /(]*href=)([^\s^>]+)*([^>]*>)/gi, + /

<\/p>/gi, + /]+)\/>|/gi + ]; + } + + // Convert strong and em to b and i in FF since it can't handle them + if (apf.isGecko) {//@todo what about the other browsers? + html = html.replace(prepareRE[0], "<$1b$2>") + .replace(prepareRE[1], "<$1i$2>"); + } + else if (apf.isIE) { + html = html.replace(prepareRE[2], "'") // IE can't handle apos + .replace(prepareRE[4], "$1$2 _apf_href=$2$3"); + //.replace(prepareRE[5], "

 

"); + + //
's need to be replaced to be properly handled as + // block elements by IE - because they're not converted + // when an editor command is executed + var str = [], capture = false, strP = [], depth = [], bdepth = [], + lastBlockClosed = false; + html.replace(prepareRE[3], + function(m, inline, close, tag, br, block, bclose, btag, any){ + if (inline) { + var id = strP.push(inline); + + tag = tag.toLowerCase(); + if (!selfClosing[tag]) { + if (close) { + if (!depth[depth.length-1] + || depth[depth.length-1][0] != tag) { + strP.length--; //ignore non matching tag + } + else { + depth.length--; + } + } + else { + depth.push([tag, id]); + } + } + + capture = true; + } + else if (any) { + strP.push(any); + capture = true; + } + else if (br) { + if (capture) { + if (depth.length) { + strP.push(br); + } + else { + str.push("

", strP.join(""), "

"); + strP = []; + } + + if (!depth.length) + capture = false; + } + else { + if ((bdepth.length || lastBlockClosed) + && br.indexOf("_apf_marker") > -1) { + //donothing + } + else + str.push("

 

"); + } + } + else if (block){ + if (bclose) { + if (bdepth[bdepth.length-1] != btag.toLowerCase()) { + return; + } + else { + bdepth.length--; + } + + //Never put P's inside block elements + if (strP.length) { + str.push(strP.join("")); + strP = []; + } + + lastBlockClosed = 2; + } + else { + var useStrP = strP.length && strP.join("").trim(); + var last = useStrP ? strP : str; + if (!noMarginTags[btag]) { + if (last[last.length - 1] == "

 

") + last[last.length - 1] = "";//

"; //maybe make this a setting + else if(useStrP && !bdepth.length) + last.push("

"); + } + + if (strP.length) { + //Never put P's inside block elements + if (!useStrP || bdepth.length) { + str.push(strP.join("")); + strP = []; + } + else { + str.push("

", strP.join(""), "

"); + strP = []; + } + } + + bdepth.push(btag.toLowerCase()); + } + + str.push(block); + capture = false; + } + + lastBlockClosed = lastBlockClosed == 2 ? 1 : false; + }); + var s; + if ((s = strP.join("")).trim()) + str.push(bNoEnclosing + ? s + : "

" + s + "

"); + html = str.join(""); + } + + // Fix some issues + html = (apf.xmlentities ? apf.xmlentities(html) : html) + .replace(prepareRE[6], ""); + + return html; + }, + + /** + * Return a string of html, but then formatted in such a way that it can + * embedded. + * + * @param {String} html + * @param {Boolean} noEntities + * @type {String} + */ + parse: function(html, noEntities, noParagraph) { + if (!exportRE) { + // compile 'em regezz + exportRE = [ + /]*><\/li>/gi, + /]*_apf_placeholder="1"\/?>/gi, + /<(a|span|div|h1|h2|h3|h4|h5|h6|pre|address)>[\s\n\r\t]*<\/(a|span|div|h1|h2|h3|h4|h5|h6|pre|address)>/gi, + /<(tr|td)>[\s\n\r\t]*<\/(tr|td)>/gi, + /[\s]*_apf_href="?[^\s^>]+"?/gi, + /(".*?"|'.*?')|(\w)=([^'"\s>]+)/gi, + /<((?:br|input|hr|img)(?:[^>]*[^\/]|))>/ig, // NO! do
see selfClosing + /

 $/mig, + /(]*?>(?:[\r\n\s]| )*]*?>)|(<(\/?)(span|strong|em|u|i|b|a|br|strike|sup|sub|font|img)(?:\s+.*?)?>)|(<(\/?)([\w\-]+)(?:\s+.*?)?>)|([^<>]*)/gi, + /<\/p>/gi, //

 <\/p>| + /

/gi, + /<\s*\/?\s*(?:\w+:\s*)?[\w-]*[\s>\/]/g + ]; + } + + if (apf.isIE) { + html = html.replace(exportRE[7], "

") + .replace(exportRE[9], "
") + .replace(exportRE[10], "") + } + else if (html == "
") + html = ""; + + html = (!noEntities && apf.xmlentities ? apf.xmlentities(html) : html) + .replace(exportRE[0], "") + .replace(exportRE[1], "") + .replace(exportRE[2], "") + .replace(exportRE[3], "<$1> ") + .replace(exportRE[4], "") + .replace(exportRE[6], "<$1 />") + .replace(exportRE[11], function(m){ + return m.toLowerCase(); + }); + + //@todo: Ruben: Maybe make this a setting (paragraphs="true") + //@todo might be able to unify this function with the one above. + if (apf.isIE && !noParagraph) { + var str = [], capture = true, strP = [], depth = [], bdepth = []; + html.replace(exportRE[8], + function(m, br, inline, close, tag, block, bclose, btag, any){ + if (inline) { + if (apf.isIE) { + inline = inline.replace(exportRE[5], + function(m1, str, m2, v){ + return str || m2 + "=\"" + v + "\""; + });//'$2="$3"') //quote un-quoted attributes + } + + var id = strP.push(inline); + + if (!selfClosing[tag]) { + if (close) { + if (!depth[depth.length-1] + || depth[depth.length-1][0] != tag) { + strP.length--; //ignore non matching tag + } + else { + depth.length--; + } + } + else { + depth.push([tag, id]); + } + } + + capture = true; + } + else if (any) { + strP.push(any); + capture = true; + } + else if (br) { + if (capture) { + if (depth.length) { + strP.push(br); + } + else { + str.push("

", strP.join("").trim() + || " ", "

"); + strP = []; + capture = false; + } + } + else + str.push("

 

"); + } + else if (block){ + if (bclose) { + if (bdepth[bdepth.length-1] != btag) { + return; + } + else { + bdepth.length--; + } + + //Never put P's inside block elements + if (strP.length) { + str.push(strP.join("")); + strP = []; + } + } + else { + if (apf.isIE) { + block = block.replace(exportRE[5], + function(m1, str, m2, v){ + return str || m2 + "=\"" + v + "\""; + });//'$2="$3"') //quote un-quoted attributes + } + + //@todo this section can be make similar to the one + // in the above function and vice verse + var last = strP.length ? strP : str; + if (last[last.length - 1] == "

 

") + last.length--; + + if (strP.length) { + var s; + //Never put P's inside block elements + if (bdepth.length || (s = strP.join("").trim()) + .replace(/<.*?>/g,"").trim() == "") { + str.push(s || strP.join("")); + strP = []; + } + else { + str.push("

", + (s || strP.join("").trim() || " ") + .replace(/
[\s\r\n]*$/, ""), + "

"); + strP = []; + } + } + + bdepth.push(btag); + } + + str.push(block); + capture = false; + } + }); + + if (strP.length) { + str.push("

" + strP.join("") + .replace(/
[\s\r\n]*$/, "") + "

"); + } + html = str.join(""); + } + else { + html = html.replace(/]*_apf_marker="1"[^>]*>/gi, "
"); + } + + + + return html; + } + }; +})(); + + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/core/lib/language.js)SIZE(8586)TIME(Fri, 14 Oct 2011 08:01:44 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/core/lib/layout.js)SIZE(13658)TIME(Fri, 14 Oct 2011 08:01:44 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + + +/** + * Object dealing with layout updates + */ +apf.layout = { + compile : function(oHtml){ + var l = this.layouts[oHtml.getAttribute("id")]; + if (!l) return false; + + var root = l.root.copy();//is there a point to copying? + + l.layout.compile(root); + l.layout.reset(); + }, + + removeAll : function(aData) { + aData.children.length = null + + var htmlId = this.getHtmlId(aData.pHtml); + if (!this.rules[htmlId]) + delete this.qlist[htmlId]; + }, + + timer : null, + qlist : {}, + dlist : [], + $hasQueue : false, + + queue : function(oHtml, obj, compile, q){ + if (!q) { + this.$hasQueue = true; + q = this.qlist; + } + + var id; + if (!(id = this.getHtmlId(oHtml))) + id = apf.setUniqueHtmlId(oHtml); + + if (q[id]) { + if (obj) + q[id][2].push(obj); + if (compile) + q[id][1] = compile; + return; + } + + q[id] = [oHtml, compile, [obj]]; + + if (!this.timer) + this.timer = apf.setZeroTimeout(function(){ + apf.layout.processQueue(); + }); + }, + + processQueue : function(){ + var i, id, l, qItem, list; + + for (i = 0; i < this.dlist.length; i++) { + if (this.dlist[i].hidden) + this.dlist[i].hide(); + else + this.dlist[i].show(); + } + + do { + var newq = {}; + var qlist = this.qlist; + this.qlist = {}; + + this.$hasQueue = false; + + for (id in qlist) { + qItem = qlist[id]; + + if (qItem[1]) + apf.layout.compileAlignment(qItem[1]); + + list = qItem[2]; + for (i = 0, l = list.length; i < l; i++) { + if (list[i]) { + if (list[i].$amlDestroyed) + continue; + //if (list[i].$amlLoaded) + list[i].$updateLayout(); + /*else + this.queue(qItem[0], list[i], null, newq);*/ + } + } + + apf.layout.activateRules(qItem[0]); + } + } while (this.$hasQueue); + + if (apf.hasSingleRszEvent) + apf.layout.forceResize(); + + this.dlist = []; + + clearTimeout(this.timer); + this.timer = null; + }, + + rules : {}, + onresize : {}, + + getHtmlId : function(oHtml){ + return oHtml.getAttribute ? oHtml.getAttribute("id") : 1; + }, + + /** + * Adds layout rules to the resize event of the browser. Use this instead + * of onresize events to add rules that specify determine the layout. + * @param {HTMLElement} oHtml the element that triggers the execution of the rules. + * @param {String} id the identifier for the rules within the resize function of this element. Use this to easily update or remove the rules added. + * @param {String} rules the javascript code that is executed when the html element resizes. + * @param {Boolean} [overwrite] whether the rules are added to the resize function or overwrite the previous set rules with the specified id. + */ + setRules : function(oHtml, id, rules, overwrite){ + if (!this.getHtmlId(oHtml)) + apf.setUniqueHtmlId(oHtml); + if (!this.rules[this.getHtmlId(oHtml)]) + this.rules[this.getHtmlId(oHtml)] = {}; + + var ruleset = this.rules[this.getHtmlId(oHtml)][id]; + if (!overwrite && ruleset) { + this.rules[this.getHtmlId(oHtml)][id] = rules + "\n" + ruleset; + } + else + this.rules[this.getHtmlId(oHtml)][id] = rules; + }, + + /** + * Retrieves the rules set for the resize event of an html element specified by an identifier + * @param {HTMLElement} oHtml the element that triggers the execution of the rules. + * @param {String} id the identifier for the rules within the resize function of this element. + */ + getRules : function(oHtml, id){ + return id + ? this.rules[this.getHtmlId(oHtml)][id] + : this.rules[this.getHtmlId(oHtml)]; + }, + + /** + * Removes the rules set for the resize event of an html element specified by an identifier + * @param {HTMLElement} oHtml the element that triggers the execution of the rules. + * @param {String} id the identifier for the rules within the resize function of this element. + */ + removeRule : function(oHtml, id){ + var htmlId = this.getHtmlId(oHtml); + if (!this.rules[htmlId]) + return; + + var ret = this.rules[htmlId][id] || false; + delete this.rules[htmlId][id]; + + var prop; + for (prop in this.rules[htmlId]) { + + } + if (!prop) + delete this.rules[htmlId] + + if (apf.hasSingleRszEvent) { + if (this.onresize[htmlId]) + this.onresize[htmlId] = null; + else { + var p = oHtml.parentNode; + while (p && p.nodeType == 1 && !this.onresize[p.getAttribute("id")]) { + p = p.parentNode; + } + + if (p && p.nodeType == 1) { + var x = this.onresize[p.getAttribute("id")]; + if (x.children) + delete x.children[htmlId] + } + } + } + + return ret; + }, + + /** + * Activates the rules set for an html element + * @param {HTMLElement} oHtml the element that triggers the execution of the rules. + */ + activateRules : function(oHtml, no_exec){ + if (!oHtml) { //!apf.hasSingleRszEvent && + var prop, obj; + for(prop in this.rules) { + obj = document.getElementById(prop); + if (!obj || obj.onresize) // || this.onresize[prop] + continue; + this.activateRules(obj); + } + + if (apf.hasSingleRszEvent && apf.layout.$onresize) + apf.layout.$onresize(); + return; + } + + var rsz, id, rule, rules, strRules = []; + if (!apf.hasSingleRszEvent) { + rules = this.rules[this.getHtmlId(oHtml)]; + if (!rules){ + oHtml.onresize = null; + return false; + } + + for (id in rules) { //might need optimization using join() + if (typeof rules[id] != "string") + continue; + strRules.push(rules[id]); + } + + //apf.console.info(strRules.join("\n")); + rsz = apf.needsCssPx + ? new Function(strRules.join("\n")) + : new Function(strRules.join("\n").replace(/ \+ 'px'|try\{\}catch\(e\)\{\}\n/g,"")) + + oHtml.onresize = rsz; + if (!no_exec) + rsz(); + } + else { + var htmlId = this.getHtmlId(oHtml); + rules = this.rules[htmlId]; + if (!rules){ + //@todo keep .children + //delete this.onresize[htmlId]; + return false; + } + + for (id in rules) { //might need optimization using join() + if (typeof rules[id] != "string") + continue; + strRules.push(rules[id]); + } + + var p = oHtml.parentNode; + while (p && p.nodeType == 1 && !this.onresize[p.getAttribute("id")]) { + p = p.parentNode; + } + + var f = new Function(strRules.join("\n"));//.replace(/try\{/g, "").replace(/}catch\(e\)\{\s*\}/g, "\n") + if (this.onresize[htmlId]) + f.children = this.onresize[htmlId].children; + + if (p && p.nodeType == 1) { + var x = this.onresize[p.getAttribute("id")]; + this.onresize[htmlId] = (x.children || (x.children = {}))[htmlId] = f; + } + else { + this.onresize[htmlId] = f; + } + if (!no_exec) + f(); + + if (!apf.layout.$onresize) { + /*var f = apf.layout.onresize; + window.onresize = function(){ + var s = []; + for (var name in f) + s.unshift(f[name]); + for (var i = 0; i < s.length; i++) + s[i](); + }*/ + + var rsz = function(f){ + //@todo fix this + try{ + var c = []; + for (var name in f) + if (f[name]) + c.unshift(f[name]); + for (var i = 0; i < c.length; i++){ + c[i](); + if (c[i].children) { + rsz(c[i].children); + } + } + } + catch(e){ + + } + } + + apf.addListener(window, "resize", apf.layout.$onresize = function(){ + rsz(apf.layout.onresize); + }); + } + } + }, + + /** + * Forces calling the resize rules for an html element + * @param {HTMLElement} oHtml the element for which the rules are executed. + */ + forceResize : function(oHtml){ + if (apf.hasSingleRszEvent) + return apf.layout.$onresize && apf.layout.$onresize(); + + /* @todo this should be done recursive, old way for now + apf.hasSingleRszEvent + ? this.onresize[this.getHtmlId(oHtml)] + : + */ + + var rsz = oHtml.onresize; + if (rsz) + rsz(); + + var els = oHtml.getElementsByTagName("*"); + for (var i = 0, l = els.length; i < l; i++) { + if (els[i] && els[i].onresize) + els[i].onresize(); + } + }, + + paused : {}, + + /** + * Disables the resize rules for the html element temporarily. + * @param {HTMLElement} oHtml the element for which the rules are paused. + * @param {Function} func the resize code that is used temporarily for resize of the html element. + */ + pause : function(oHtml, replaceFunc){ + if (apf.hasSingleRszEvent) { + var htmlId = this.getHtmlId(oHtml); + this.paused[htmlId] = this.onresize[htmlId] || true; + + if (replaceFunc) { + this.onresize[htmlId] = replaceFunc; + this.onresize[htmlId].children = this.paused[htmlId].children; + replaceFunc(); + } + else + delete this.onresize[htmlId]; + } + else { + this.paused[this.getHtmlId(oHtml)] = oHtml.onresize || true; + + if (replaceFunc) { + oHtml.onresize = replaceFunc; + replaceFunc(); + } + else + oHtml.onresize = null; + } + }, + + /** + * Enables paused resize rules for the html element + * @param {HTMLElement} oHtml the element for which the rules have been paused. + */ + play : function(oHtml){ + if (!this.paused[this.getHtmlId(oHtml)]) + return; + + if (apf.hasSingleRszEvent) { + var htmlId = this.getHtmlId(oHtml); + var oldFunc = this.paused[htmlId]; + if (typeof oldFunc == "function") { + this.onresize[htmlId] = oldFunc; + //oldFunc(); + } + else + delete this.onresize[htmlId]; + + if (apf.layout.$onresize) + apf.layout.$onresize(); + + this.paused[this.getHtmlId(oHtml)] = null; + } + else { + var oldFunc = this.paused[this.getHtmlId(oHtml)]; + if (typeof oldFunc == "function") { + oHtml.onresize = oldFunc; + oldFunc(); + } + else + oHtml.onresize = null; + + this.paused[this.getHtmlId(oHtml)] = null; + } + } +}; + + +/** + * @private + */ +apf.getWindowWidth = function(){ + return apf.isIE ? document.documentElement.offsetWidth - apf.windowHorBorder : window.innerWidth; +} +/** + * @private + */ +apf.getWindowHeight = function(){ + return apf.isIE ? document.documentElement.offsetHeight - apf.windowVerBorder : window.innerHeight; +} + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/core/lib/printer.js)SIZE(5120)TIME(Fri, 14 Oct 2011 08:01:44 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/core/lib/queue.js)SIZE(2951)TIME(Wed, 30 Nov 2011 17:29:20 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + + +// Only add setZeroTimeout to the window object, and hide everything +// else in a closure. +apf.setZeroTimeout = !window.postMessage + ? function() { $setTimeout.apply(null, arguments); } + : (function() { + var timeouts = []; + var messageName = "zero-timeout-message"; + + // Like setTimeout, but only takes a function argument. There's + // no time argument (always zero) and no arguments (you have to + // use a closure). + function setZeroTimeout(fn) { + timeouts.push(fn); + window.postMessage(messageName, "*"); + } + + function handleMessage(e) { + if (!e) e = event; + if (e.source == window && e.data == messageName) { + apf.stopPropagation(e); + if (timeouts.length > 0) + timeouts.shift()(); + } + } + + apf.addListener(window, "message", handleMessage, true); + + // Add the one thing we want added to the window object. + return setZeroTimeout; + })(); + + + + +/** + * + */ +apf.queue = { + //@todo apf3.0 + q : {}, + + timer : null, + add : function(id, f){ + this.q[id] = f; + if (!this.timer) + this.timer = apf.setZeroTimeout(function(){ + apf.queue.empty(); + }); + }, + + remove : function(id){ + delete this.q[id]; + }, + + empty : function(prop){ + clearTimeout(this.timer); + this.timer = null; + + + if (apf.layout && apf.layout.$hasQueue) + apf.layout.processQueue(); + + + if (apf.xmldb && apf.xmldb.$hasQueue) + apf.xmldb.notifyQueued(); + + + var q = this.q; + this.q = {}; + for (var prop in q){ + var f = q[prop]; + if (f) { + delete q[prop]; + f(); + } + } + } +}; + + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/core/lib/resize.js)SIZE(13139)TIME(Fri, 14 Oct 2011 08:01:44 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + + +/** + * This abstraction is using for resizing block elements. Resizing is allowed + * with square elements in vertical, horizontal or both planes. Symmetric + * resizing is possible with SHIFT button. + * + * @private + * @default_private + * @constructor + * + * @author Lukasz Lipinski + * @version %I%, %G% + * @since 1.0 + * @namespace apf + */ + +apf.resize = function() { + /** + * {Boolean} scalex resizing in horizontal plane, default is true + * Possible values: + * true resizing in horizontal plane is allowed + * false resizing in horizontal plane is not allowed + * {Boolean} scaley resizing in vertical plane, default is true + * Possible values: + * true resizing in vertical plane is allowed + * false resizing in vertical plane is not allowed + * {Boolean} scaleratio resizing in horizontal or vertical plane only is not allowed. Resizing in two dimensions plane at the same time is allowed. + * Possible values: + * true resizing in two dimensions plane at the same time is allowed + * false Resizing in two dimensions plane at the same time is not allowed + * {Number} dwidth the minimal horizontal size of Block element, default is 56 pixels + * {Number} dheight the minimal vertical size of Block element, default is 56 pixels + */ + this.scales = { + scalex : false, + scaley : false, + scaleratio: false, + dwidth : 0, + dheight : 0, + snap : false, + gridW : 48, + gridH : 48 + }; + + /** + * html representation of resized block element + */ + this.htmlElement; + + /** + * store object representations of inputs elements + */ + var squares = []; + + this.init = function() { + squares = [ + new apf.resize.square("top", "left", this), + new apf.resize.square("top", "middle", this), + new apf.resize.square("top", "right", this), + new apf.resize.square("middle", "left", this), + new apf.resize.square("middle", "right", this), + new apf.resize.square("bottom", "left", this), + new apf.resize.square("bottom", "middle", this), + new apf.resize.square("bottom", "right", this)]; + }; + + /** + * Links block element with resize feature + * + * @param {HTMLElement} oHtml html representation of block element + * @param {Object} scales blocks scale settings + */ + this.grab = function(oHtml, scales) { + this.htmlElement = oHtml; + this.scales = scales; + + if (!squares.length) + this.init(); + this.show(); + }; + + /** + * Hides all block squares + */ + this.hide = function() { + for (var i = 0, l = squares.length; i < l; i++) { + squares[i].visible = false; + squares[i].repaint(); + } + }; + + /** + * Shows all block squares + */ + this.show = function() { + var sx = this.scales.scalex; + var sy = this.scales.scaley; + var sr = this.scales.scaleratio; + + for (var i = 0, l = squares.length, s; i < l; i++) { + s = squares[i]; + s.visible = sx && sy + ? true + : (sy && !sx + ? (s.posX == "middle" + ? true + : false) + : (sx && !sy + ? (s.posY == "middle" + ? true + : false) + : (sr + ? ((s.posY == "top" || s.posY == "bottom") + && s.posX !== "middle" + ? true + : false) + : false))); + + s.repaint(); + } + }; + + /** + * Destroys all block squares + */ + this.destroy = function(){ + for (var i = 0; i < squares.length; i++) { + squares[i].destroy(); + } + }; +}; + +/** + * Creates html and object representation for square element. Square is used for + * resizing block elements. + * + * @param {String} posY square vertical align relative to resized block element + * Possible values: + * top square is on top of resized block element + * middle square is in the middle of the resized block element + * bottom square is on the bottom of resized block element + * @param {String} posX square vertical align relative to resized block element + * Possible values: + * left square is on the left of resized block element + * middle square is in the middle of the resized block element + * right square is on the right of resized block element + * @param {Object} objResize object of resize class + * @constructor + */ +apf.resize.square = function(posY, posX, objResize) { + /** + * Square visibility + */ + this.visible = true; + /** + * square vertical align relative to resized block element + */ + this.posX = posX; + /** + * square vertical align relative to resized block element + */ + this.posY = posY; + + var margin = 0; + var _self = this; + + /** + * html represenation of square element + */ + this.htmlElement = objResize.htmlElement.parentNode.appendChild(document.createElement('div')); + apf.setStyleClass(this.htmlElement, "square"); + + /** + * Repaints square + */ + this.repaint = function() { + if (this.visible) { + var block = objResize.htmlElement; + this.htmlElement.style.display = "block"; + + var bw = parseInt(block.style.width) + apf.getDiff(block)[0]; + var bh = parseInt(block.style.height) + apf.getDiff(block)[1]; + var bt = parseInt(block.style.top); + var bl = parseInt(block.style.left); + + var sw = this.htmlElement.offsetWidth; + var sh = this.htmlElement.offsetHeight; + + var t = posY == "top" + ? bt - margin - sh + : posY == "middle" + ? bt + bh/2 - sh/2 + : bt + bh + margin; + var l = posX == "left" + ? bl - margin - sw + : posX == "middle" + ? bl + bw/2 - sw/2 + : bl + bw + margin; + + var c = (posY == "middle" + ? "w-resize" + : (posX == "middle" + ? "n-resize" + : (posY + posX == "topleft" + || posY + posX == "bottomright") + ? "nw-resize" + : "ne-resize")); + + this.htmlElement.style.top = (t - 1) + "px"; + this.htmlElement.style.left = (l - 1) + "px"; + this.htmlElement.style.cursor = c; + } + else { + //IE bug + var sw = this.htmlElement.offsetWidth; + this.htmlElement.style.display = 'none'; + } + }; + + this.destroy = function(){ + apf.destroyHtmlNode(this.htmlElement); + }; + + /* Events */ + this.htmlElement.onmouseover = function(e) { + apf.setStyleClass(_self.htmlElement, "squareHover"); + }; + + this.htmlElement.onmouseout = function(e) { + apf.setStyleClass(_self.htmlElement, "", ["squareHover"]); + }; + + this.htmlElement.onmousedown = function(e) { + e = (e || event); + + var block = objResize.htmlElement, + + sx = e.clientX, + sy = e.clientY, + + pt = block.parentNode.offsetTop, + pl = block.parentNode.offsetLeft, + + dw = objResize.scales.dwidth, + dh = objResize.scales.dheight, + + snap = objResize.scales.snap, + gridH = objResize.scales.gridH, + gridW = objResize.scales.gridW, + + objBlock = apf.flow.isBlock(block), + r = objBlock.other.ratio, + + posX = _self.posX, + posY = _self.posY, + + width, height, top, left, dx, dy, + prev_w, prev_h, + + l = parseInt(block.style.left), + t = parseInt(block.style.top), + w = parseInt(block.style.width), + h = parseInt(block.style.height), + resized = false; + + objResize.onresizedone(w, h, t, l); + + if (e.preventDefault) { + e.preventDefault(); + } + + document.onmousemove = function(e) { + e = (e || event); + + dx = e.clientX - sx; + dy = e.clientY - sy; + var shiftKey = e.shiftKey, + proportion = r; + + if (shiftKey) { + if (posX == "right" && posY == "bottom") { + width = w + dx; + height = width/proportion; + left = l; + top = t; + } + else if (posX == "right" && posY == "top") { + width = w + dx; + height = width/proportion; + left = l; + top = t - dx/proportion; + } + else if (posX == "left" && posY == "bottom") { + width = w - dx; + height = width/proportion; + left = l + dx; + top = t; + } + else if (posX == "left" && posY == "top") { + width = w - dx; + height = width/proportion; + left = l + dx; + top = t + dx/proportion; + } + + /* Keep minimal size */ + if(width >= dw && height >= dh) { + width = prev_w = Math.max(dw, width); + height = prev_h = Math.max(dh, height); + } + else { + width = prev_w; + height = prev_h; + return false; + } + } + else { + width = posX == "right" + ? w + dx + : (posX == "left" + ? w - dx + : w); + height = posY == "bottom" + ? h + dy + : (posY == "top" + ? h - dy + : h); + left = posX == "right" + ? l + : (posX == "left" + ? Math.min(l + w - dw, l + dx) + : l); + top = posY == "bottom" + ? t + : (posY == "top" + ? Math.min(t + h - dh, t + dy) + : t); + + /* Keep minimal size */ + width = Math.max(dw, width); + height = Math.max(dh, height); + } + + if (snap) { + left = Math.floor(left / gridW) * gridW; + top = Math.floor(top / gridH) * gridH; + width = Math.ceil(width / gridW) * gridW; + height = Math.ceil(height / gridH) * gridH; + } + + if (objResize.onresize) { + objResize.onresize(block, top, left, width, height); + } + + objResize.show(); + + resized = true; + }; + + document.onmouseup = function(e) { + document.onmousemove = null; + if (objResize.onresizedone && resized) { + objResize.onresizedone(width, height, top, left); + objBlock.other.ratio = width / height; + resized = false; + } + }; + }; +} + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/core/lib/resize2.js)SIZE(10417)TIME(Fri, 14 Oct 2011 08:01:44 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/core/lib/selection.js)SIZE(32184)TIME(Fri, 14 Oct 2011 08:01:44 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/core/lib/skins.js)SIZE(12336)TIME(Tue, 13 Dec 2011 13:33:58 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + + +/** + * @private + */ +apf.skins = { + skins : {}, + css : [], + events : ["onmousemove", "onmousedown", "onmouseup", "onmouseout", + "onclick", "ondragcopy", "ondragstart", "ondblclick"], + + /* *********** + Init + ************/ + Init: function(xmlNode, refNode, path){ + /* + get data from refNode || xmlNode + - name + - icon-path + - media-path + + all paths of the xmlNode are relative to the src attribute of refNode + all paths of the refNode are relative to the index.html + images/ is replaced if there is a refNode to the relative path from index to the skin + /images/ + */ + var name = (refNode ? refNode.getAttribute("id") : null) + || xmlNode.getAttribute("id"); + var base = (refNode ? (refNode.getAttribute("src") || "").match(/\//) || path : "") + ? (path || refNode.getAttribute("src")).replace(/\/[^\/]*$/, "") + "/" + : ""; //@todo make this absolute? + + var mediaPath = null, iconPath = null; + mediaPath = xmlNode.getAttribute("media-path"); + if (mediaPath !== null) + mediaPath = apf.getAbsolutePath(base || apf.hostPath, mediaPath); + else if (refNode) { + mediaPath = refNode.getAttribute("media-path"); + if (mediaPath !== null) + mediaPath = apf.getAbsolutePath(apf.hostPath, mediaPath); + else + mediaPath = apf.getAbsolutePath(base || apf.hostPath, "images/"); + } + + iconPath = xmlNode.getAttribute("icon-path"); + if (iconPath !== null) + iconPath = apf.getAbsolutePath(base || apf.hostPath, iconPath); + else if (refNode) { + iconPath = refNode.getAttribute("icon-path"); + if (iconPath !== null) + iconPath = apf.getAbsolutePath(apf.hostPath, iconPath); + else + iconPath = apf.getAbsolutePath(base || apf.hostPath, "icons/"); + } + + if (!name) + name = "default"; + + if (xmlNode.getAttribute("id")) + document.body.className += " " + xmlNode.getAttribute("id"); + + var names = name.split("|"); + name = names[0]; + + if (!this.skins[name] || name == "default") { + this.skins[name] = { + base : base, + name : name, + iconPath : iconPath, + mediaPath: mediaPath, + templates: {}, + originals: {}, + xml : xmlNode + } + + if (names.length > 1) { + for (var i = 0; i < names.length; i++) + this.skins[names[i]] = this.skins[name]; + } + } + + if (!this.skins["default"] && this.$first == refNode) + this.skins["default"] = this.skins[name]; + + var nodes = xmlNode.childNodes; + for (var i = nodes.length - 1; i >= 0; i--) { + if (nodes[i].nodeType != 1) + continue; + + //this.templates[nodes[i].tagName] = nodes[i]; + this.skins[name].templates[nodes[i].getAttribute("name")] = nodes[i]; + if (nodes[i].ownerDocument) + this.importSkinDef(nodes[i], base, name); + } + + this.purgeCss(mediaPath, iconPath); + + if (this.queue[name]) { + for (var prop in this.queue[name]) { + this.queue[name][prop](); + } + } + }, + + /** + * This method loads a stylesheet from a url + * @param {String} filename Required The url to load the stylesheet from + * @param {String} title Optional Title of the stylesheet to load + * @method + */ + loadStylesheet: function(filename, title){ + var o; + with (o = document.getElementsByTagName("head")[0].appendChild(document.createElement("LINK"))) { + rel = "stylesheet"; + type = "text/css"; + href = filename; + title = title; + } + + return o; + }, + + /* *********** + Import + ************/ + importSkinDef: function(xmlNode, basepath, name){ + var i, l, nodes = $xmlns(xmlNode, "style", apf.ns.aml), tnode, node; + for (i = 0, l = nodes.length; i < l; i++) { + node = nodes[i]; + + if (node.getAttribute("src")) + this.loadStylesheet(apf.getAbsolutePath(basepath, node.getAttribute("src"))); + else { + var test = true; + if (node.getAttribute("condition")) { + try { + test = eval(node.getAttribute("condition")); + } + catch (e) { + test = false; + } + } + + if (test) { + //#-ifndef __PROCESSED + tnode = node.firstChild; + while (tnode) { + this.css.push(tnode.nodeValue); + tnode = tnode.nextSibling; + } + /*#-else + this.css.push(nodes[i].firstChild.nodeValue); + #-endif*/ + } + } + } + + nodes = $xmlns(xmlNode, "alias", apf.ns.apf); + var t = this.skins[name].templates; + for (i = 0; i < nodes.length; i++) { + if (!nodes[i].firstChild) + continue; + t[nodes[i].firstChild.nodeValue.toLowerCase()] = xmlNode; + } + }, + + loadedCss : "", + purgeCss: function(imagepath, iconpath){ + if (!this.css.length) + return; + + var cssString = this.css.join("\n").replace(/images\//g, imagepath).replace(/icons\//g, iconpath); + apf.importCssString(cssString); + + + + this.css = []; + }, + + loadCssInWindow : function(skinName, win, imagepath, iconpath){ + this.css = []; + var name = skinName.split(":"); + var skin = this.skins[name[0]]; + var template = skin.templates[name[1]]; + this.importSkinDef(template, skin.base, skin.name); + var cssString = this.css.join("\n").replace(/images\//g, imagepath).replace(/icons\//g, iconpath); + apf.importCssString(cssString); + + this.css = []; + }, + + /* *********** + Retrieve + ************/ + setSkinPaths: function(skinName, amlNode){ + skinName = skinName.split(":"); + var name = skinName[0]; + var type = skinName[1]; + + + + amlNode.iconPath = this.skins[name].iconPath; + amlNode.mediaPath = this.skins[name].mediaPath; + }, + + getTemplate: function(skinName, noError){ + skinName = skinName.split(":"); + var name = skinName[0]; + var type = skinName[1]; + + if (!this.skins[name]) { + if (noError) + return false; + + + + return false; + } + + if (!this.skins[name].templates[type]) + return false; + + var skin = this.skins[name].templates[type]; + var originals = this.skins[name].originals[type]; + if (!originals) { + originals = this.skins[name].originals[type] = {}; + + + + var nodes = $xmlns(skin, "presentation", apf.ns.aml)[0].childNodes; + for (var i = 0; i < nodes.length; i++) { + if (nodes[i].nodeType != 1) continue; + originals[nodes[i].baseName || nodes[i][apf.TAGNAME]] = nodes[i]; + } + } + + /*for (var item in originals) { + pNodes[item] = originals[item]; + }*/ + + return originals; + }, + + getCssString : function(skinName){ + return apf.queryValue($xmlns(this.skins[skinName.split(":")[0]].xml, + "style", apf.ns.aml)[0], "text()"); + }, + + + changeSkinset : function(value){ + var node = apf.document.documentElement; + while (node) { + if (node && node.nodeFunc == apf.NODE_VISIBLE + && node.hasFeature(apf.__PRESENTATION__) && !node.skinset) { + node.$propHandlers["skinset"].call(node, value);//$forceSkinChange + node.skinset = null; + } + + //Walk tree + if (node.firstChild || node.nextSibling) { + node = node.firstChild || node.nextSibling; + } + else { + do { + node = node.parentNode; + } while (node && !node.nextSibling) + + if (node) + node = node.nextSibling; + } + } + }, + + + queue : {}, + waitForSkin : function(skinset, id, callback){ + if (this.skins[skinset]) + return; + + (this.queue[skinset] || (this.queue[skinset] = {}))[id] = callback; + return true; + }, + + + + setIcon : function(oHtml, strQuery, iconPath){ + if (!strQuery) { + oHtml.style.backgroundImage = ""; + return; + } + + if (oHtml.tagName.toLowerCase() == "img") { + oHtml.setAttribute("src", strQuery + ? (iconPath || "") + strQuery + : ""); + return; + } + + + + //Assuming image url + { + + + oHtml.style.backgroundImage = "url(" + (iconPath || "") + + strQuery + ")"; + } + } +}; + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/core/lib/sort.js)SIZE(8239)TIME(Fri, 14 Oct 2011 08:01:44 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + + +/** + * Object handling sorting in a similar way as xslt. + * + * @constructor + * + * @author Ruben Daniels (ruben AT ajax DOT org) + * @version %I%, %G% + * @since 0.8 + * + * @private + */ +apf.Sort = function(xmlNode){ + var settings = {}; + + //use this function to parse the each node + this.parseXml = function(xmlNode, clear){ + if (clear) settings = {}; + + settings.order = xmlNode.order; + settings.getValue = xmlNode.csort || xmlNode.$compile("sort"); + settings.getNodes = self[xmlNode["nodes-method"]]; + + settings.ascending = (settings.order || "").indexOf("desc") == -1; + settings.order = null; + + if (xmlNode["data-type"]) + settings.method = sort_methods[xmlNode["data-type"]]; + else if (xmlNode["sort-method"]) { + settings.method = self[xmlNode["sort-method"]]; + + + } + else + settings.method = sort_methods["alpha"]; + + var str = xmlNode["date-format"]; + if (str) { + settings.sort_dateFmtStr = str; + settings.method = sort_methods["date"]; + var result = str.match(/(D+|Y+|M+|h+|m+|s+)/g); + if (result) { + for (var pos = {}, i = 0; i < result.length; i++) + pos[result[i].substr(0, 1)] = i + 1; + settings.dateFormat = new RegExp(str.replace(/([^\sDYMhms])/g, '\\$1') + .replace(/YYYY/, "(\\d\\d\\d\\d)") + .replace(/(DD|YY|MM|hh|mm|ss)/g, "(\\d\\d)")); + settings.dateReplace = "$" + pos["M"] + "/$" + pos["D"] + "/$" + pos["Y"]; + if (pos["h"]) + settings.dateReplace += " $" + pos["h"] + ":$" + pos["m"] + ":$" + pos["s"]; + } + } + }; + + this.set = function(struct, clear){ + if (clear) settings = {}; + + apf.extend(settings, struct); + + if (settings.ascending == undefined) + settings.ascending = struct.order + ? struct.order.indexOf("desc") == -1 + : true; + + settings.order = null; + + if (struct["type"]) + settings.method = sort_methods[struct["type"]]; + else if (struct["method"]) + settings.method = self[struct["method"]]; + else if (!settings.method) + settings.method = sort_methods["alpha"]; + + if (struct.format) { + settings.sort_dateFmtStr = struct.format; + //settings.method = sort_methods["date"]; + var result = str.match(/(D+|Y+|M+|h+|m+|s+)/g); + if (result) { + for (var pos = {}, i = 0; i < result.length; i++) + pos[result[i].substr(0, 1)] = i + 1; + settings.dateFormat = new RegExp(str.replace(/([^\sDYMhms])/g, '\\$1') + .replace(/YYYY/, "(\\d\\d\\d\\d)") + .replace(/(DD|YY|MM|hh|mm|ss)/g, "(\\d\\d)")); + settings.dateReplace = "$" + pos["M"] + "/$" + pos["D"] + "/$" + pos["Y"]; + if (pos["h"]) + settings.dateReplace += " $" + pos["h"] + ":$" + pos["m"] + ":$" + pos["s"]; + } + } + + if (!settings.getValue) { + settings.getValue = function(item){ + return apf.queryValue(item, settings.xpath); + } + } + }; + + this.get = function(){ + return apf.extend({}, settings); + }; + + //use this function in __xmlUpdate [this function isnt done yet] + this.findSortSibling = function(pNode, xmlNode){ + var nodes = getNodes ? getNodes(pNode, xmlNode) : this.getTraverseNodes(pNode); + + for (var i = 0; i < nodes.length; i++) + if (!compare(xmlNode, nodes[i], true, sortSettings)) + return nodes[i]; + + return null; + }; + + // Sorting methods for sort() + var sort_intmask = ["", "0", "00", "000", "0000", "00000", "000000", + "0000000", "00000000", "000000000", "0000000000", "00000000000", + "000000000000", "0000000000000", "00000000000000"]; + var sort_methods = { + "alpha" : function (n){ + return n.toString().toLowerCase() + }, + + "number" : function (t){ + return (t.length < sort_intmask.length + ? sort_intmask[sort_intmask.length - t.length] + : "") + t; + }, + + "date" : function (t, args){ + var sort_dateFormat = settings.dateFormat; + var sort_dateReplace = settings.dateReplace; + var sort_dateFmtStr = settings.sort_dateFmtStr; + + var d;//|| (args && sort_dateFmtStr != args[0]) + if (!sort_dateFormat) { + d = new Date(t); + } + else if (sort_dateFmtStr == '*') + d = apf.date.getDateTime(t); + else + d = (new Date(t.replace(sort_dateFormat, sort_dateReplace))).getTime(); + t = "" + d.getTime();//parseInt(d); + if (t == "NaN") + t = "0"; + return (t.length < sort_intmask.length ? sort_intmask[sort_intmask.length + - t.length] : "") + t; + } + }; + + /* + sort(xpath, sort_xpath, sort_alpha, boolDesc, from, len) + jsort(n,f,p,ps,sm,desc,sp,ep) + */ + //var order, xpath, type, method, getNodes, dateFormat, dateReplace, sort_dateFmtStr, getValue; + this.apply = function(n, args, func, start, len){ + var sa = [], i = n.length; + + // build string-sortable list with sort method + while (i--) { + var v = settings.getValue(n[i]); + if (n) + sa[sa.length] = { + toString: function(){ + return this.v; + }, + xmlNode : n[i], + v : (settings.method || sort_methods.alpha)(v || "", args, n[i]) + }; + } + + // sort it + sa.sort(); + + //iterate like foreach + var end = len ? Math.min(sa.length, start + len) : sa.length; + if (!start) + start = 0; + + if (func) { + if (settings.ascending) + for (i = start; i < end; i++) + f(i, end, sa[i].xmlNode, sa[i].v); + else + for (i = end - 1; i >= start; i--) + f(end - i - 1, end, sa[i].xmlNode, sa[i].v); + } + else { + //this could be optimized by reusing n... time it later + var res = []; + if (settings.ascending) + for (i = start; i < end; i++) + res[res.length] = sa[i].xmlNode; + else + for (i = end - 1; i >= start; i--) + res[res.length] = sa[i].xmlNode; + return res; + } + }; + + if (xmlNode) + this.parseXml(xmlNode); +}; + + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/core/lib/storage.js)SIZE(9036)TIME(Fri, 14 Oct 2011 08:01:44 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/core/lib/tween.js)SIZE(35554)TIME(Tue, 13 Dec 2011 13:33:58 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + + +/** + * The animation library that is used for the animations inside elements + * @default_private + */ +apf.tween = (function(apf) { + +var modules = { + //Animation Modules + left: function(oHtml, value){ + oHtml.style.left = value + PX; + }, + right: function(oHtml, value){ + oHtml.style.left = ""; + oHtml.style.right = value + PX; + }, + top: function(oHtml, value){ + oHtml.style.top = value + PX; + }, + bottom: function(oHtml, value){ + oHtml.style.top = ""; + oHtml.style.bottom = value + PX; + }, + width: function(oHtml, value, center){ + oHtml.style.width = value + PX; + }, + height: function(oHtml, value, center){ + oHtml.style.height = value + PX; + }, + scrollTop: function(oHtml, value, center){ + oHtml.scrollTop = value; + }, + scrollLeft: function(oHtml, value, center){ + oHtml.scrollLeft = value; + }, + "height-rsz": function(oHtml, value, center){ + oHtml.style.height = value + PX; + if (apf.hasSingleResizeEvent && apf.layout.$onresize) + apf.layout.$onresize(); + }, + mwidth: function(oHtml, value, info) { + var diff = apf.getDiff(oHtml); + oHtml.style.width = value + PX; + oHtml.style.marginLeft = -1 * (value / 2 + (parseInt(apf.getStyle(oHtml, + "borderLeftWidth")) || diff[0]/2) + (info.margin || 0)) + PX; + }, + mheight: function(oHtml, value, info) { + var diff = apf.getDiff(oHtml); + oHtml.style.height = value + PX; + oHtml.style.marginTop = (-1 * value / 2 - (parseInt(apf.getStyle(oHtml, + "borderTopWidth")) || diff[1]/2) + (info.margin || 0)) + PX; + }, + scrollwidth: function(oHtml, value){ + oHtml.style.width = value + PX; + oHtml.scrollLeft = oHtml.scrollWidth; + }, + scrollheight_old: function(oHtml, value){ + try { + oHtml.style.height = value + PX; + oHtml.scrollTop = oHtml.scrollHeight; + } + catch (e) { + alert(value) + } + }, + scrollheight: function(oHtml, value, info){ + var diff = apf.getHeightDiff(oHtml), + oInt = info.$int || oHtml; + + oHtml.style.height = Math.max((value + (info.diff || 0)), 0) + PX; + oInt.scrollTop = oInt.scrollHeight - oInt.offsetHeight - diff + + (info.diff || 0) - (apf.isGecko ? 16 : 0); //@todo where does this 16 come from?? + }, + scrolltop: function(oHtml, value){ + oHtml.style.height = value + PX; + oHtml.style.top = (-1 * value - 2) + PX; + oHtml.scrollTop = 0;//oHtml.scrollHeight - oHtml.offsetHeight; + }, + clipright: function(oHtml, value, center){ + oHtml.style.clip = "rect(auto, auto, auto, " + value + "px)"; + oHtml.style.marginLeft = (-1 * value) + PX; + }, + fade: function(oHtml, value){ + if (!apf.supportOpacity && apf.hasStyleFilters) + oHtml.style.filter = value == 1 ? "" : "alpha(opacity=" + parseInt(value * 100) + ")"; + else + oHtml.style.opacity = value; + }, + bgcolor: function(oHtml, value){ + oHtml.style.backgroundColor = value; + }, + textcolor: function(oHtml, value){ + oHtml.style.color = value; + }, + htmlcss : function(oHtml, value, obj){ + if (apf.hasStyleFilters && obj.type == "filter") + oHtml.style.filter = value == 1 ? "" : "progid:DXImageTransform.Microsoft.Alpha(opacity=" + value + ")"; + else + oHtml.style[obj.type] = value + (obj.needsPx ? PX : ""); + }, + transformscale: function(oHtml, value, obj) { + oHtml.style[obj.type] = SCALEA + parseFloat(value) + SCALEB; + }, + transformrotate: function(oHtml, value, obj) { + oHtml.style[obj.type] = ROTATEA + parseFloat(value) + ROTATEB; + }, + transformvalscale: function(value) { + return SCALEA + parseFloat(value) + SCALEB; + }, + transformvalrotate: function(value) { + return ROTATEA + parseFloat(value) + ROTATEB; + } +}; + +var ID = "id", + PX = "px", + NUM = "number", + TRANSVAL = "transformval", + TRANSFORM = "transform", + SCALE = "scale", + SCALEA = "scale(", + ROTATEA = "rotate(", + SCALEB = ")", + ROTATEB = "deg)", + CSSTIMING = ["linear", "ease-in", "ease-out", "ease", "ease-in-out", "cubic-bezier"], + CSSPROPS = { + "left" : "left", + "right" : "right", + "top" : "top", + "bottom" : "bottom", + "width" : "width", + "height" : "height", + "scrollTop" : false, + "scrollLeft" : false, + "mwidth" : false, + "mheight" : false, + "scrollwidth" : false, + "scrollheight": false, + "fade" : "opacity", + "opacity" : "opacity", + "bgcolor" : "background-color", + "textcolor" : "color", + "transform" : "transform" + }, + __pow = Math.pow, + __round = Math.round, + + queue = {}, + + current= null, + + setQueue = function(oHtml, stepFunction){ + var id = oHtml.getAttribute(ID); + if (!id) { + apf.setUniqueHtmlId(oHtml); + id = oHtml.getAttribute(ID); + } + + if (!queue[id]) + queue[id] = []; + + queue[id].push(stepFunction); + if (queue[id].length == 1) + stepFunction(0); + }, + + nextQueue = function(oHtml){ + var q = queue[oHtml.getAttribute(ID)]; + if (!q) return; + + q.shift(); //Remove current step function + + if (q.length) + q[0](0); + }, + + clearQueue = function(oHtml, bStop){ + var q = queue[oHtml.getAttribute(ID)]; + if (!q) return; + + if (bStop && current && current.control) + current.control.stop = true; + q.length = 0; + }, + + purgeQueue = function(oHtml) { + var id = oHtml.getAttribute(ID); + if (!id) { + apf.setUniqueHtmlId(oHtml); + id = oHtml.getAttribute(ID); + } + + for (var i in queue) { + if (i == id) + queue[i] = []; + } + }, + + /** + * Calculates all the steps of an animation between a + * begin and end value based on 3 tween strategies + */ + calcSteps = function(func, fromValue, toValue, nrOfSteps){ + var i = 0, + l = nrOfSteps - 1, + steps = [fromValue]; + + // backward compatibility... + if (typeof func == NUM) { + if (!func) + func = apf.tween.linear; + else if (func == 1) + func = apf.tween.easeInCubic; + else if (func == 2) + func = apf.tween.easeOutCubic; + } + + /* + func should have the following signature: + func(t, x_min, dx) + where 0 <= t <= 1, dx = x_max - x_min + + easeInCubic: function(t, x_min, dx) { + return dx * pow(t, 3) + x_min; + } + */ + for (i = 0; i < l; ++i) + steps.push(func(i / nrOfSteps, fromValue, toValue - fromValue)); + steps.push(toValue); + + return steps; + }, + + /** + * Calculates all the steps of an animation between a + * begin and end value for colors + */ + calcColorSteps = function(animtype, fromValue, toValue, nrOfSteps){ + var d2, d1, + c = apf.color.colorshex, + a = parseInt((c[fromValue] || fromValue).slice(1), 16), + b = parseInt((c[toValue] || toValue).slice(1), 16), + i = 0, + out = []; + + for (; i < nrOfSteps; i++){ + d1 = i / (nrOfSteps - 1), d2 = 1 - d1; + out[out.length] = "#" + ("000000" + + ((__round((a & 0xff) * d2 + (b & 0xff) * d1) & 0xff) | + (__round((a & 0xff00) * d2 + (b & 0xff00) * d1) & 0xff00) | + (__round((a & 0xff0000) * d2 + (b & 0xff0000) * d1) & 0xff0000)).toString(16)).slice(-6); + } + + return out; + }, + + /** + * Tweens a single property of a single element or html element from a + * start to an end value. + * Example: + * + * apf.tween.single(myDiv, { + * type : "left", + * from : 10, + * to : 100, + * anim : apf.tween.EASEIN + * }); + * + * Example: + * Multiple animations can be run after eachother + * by calling this function multiple times. + * + * apf.tween.single(myDiv, options).single(myDiv2, options2); + * + * @param {Element} oHtml the object to animate. + * @param {Object} info the animation settings. + * Properties: + * {String} type the property to be animated. These are predefined + * property handlers and can be added by adding a + * method to apf.tween with the name of the property + * modifier. Default there are several handlers available. + * Possible values: + * left Sets the left position + * right Sets the right position + * top Sets the top position + * bottom Sets the bottom position + * width Sets the horizontal size + * height Sets the vertical size + * scrollTop Sets the scoll position + * mwidth Sets the width and the margin-left to width/2 + * mheight Sets the height ant the margin-top to height/2 + * scrollwidth Sets the width an sets the scroll to the maximum size + * scrollheight Sets the height an sets the scroll to the maximum size + * scrolltop Sets the height and the top as the negative height value + * fade Sets the opacity property + * bgcolor Sets the background color + * textcolor Sets the text color + * {Number, String} from the start value of the animation + * {Number, String} to the end value of the animation + * {Number} [steps] the number of steps to divide the tween in + * {Number} [interval] the time between each step + * {Number} [anim] the distribution of change between the step over the entire animation + * {Boolean} [color] whether the specified values are colors + * {Mixed} [userdata] any data you would like to have available in your callback methods + * {Function} [onfinish] a function that is called at the end of the animation + * {Function} [oneach] a function that is called at each step of the animation + * {Object} [control] an object that can stop the animation at any point + * Methods: + * stop set on the object passed . + */ + single = function(oHtml, info){ + info = apf.extend({steps: 10, interval: 5, anim: apf.tween.linear, control: {}}, info); + info.steps = Math.ceil(info.steps * apf.animSteps); + info.interval = Math.ceil(info.interval * apf.animInterval); + + if (oHtml.nodeFunc > 100) { + info.$int = oHtml.$int; + oHtml = oHtml.$ext; + } + try { //@TODO hack where currentStyle is still undefined + if ("fixed|absolute|relative".indexOf(apf.getStyle(oHtml, "position")) == -1) + oHtml.style.position = "relative"; + } catch(e){} + + var useCSSAnim = (apf.supportCSSAnim && apf.supportCSSTransition && CSSPROPS[info.type]), + isTransform = (info.type == TRANSFORM); + + info.method = useCSSAnim ? info.type : isTransform + ? modules[TRANSFORM + (info.subType || SCALE)] + : modules[info.type] + ? modules[info.type] + : (info.needsPx = needsPix[info.type] || false) + ? modules.htmlcss + : modules.htmlcss; + + + + if (useCSSAnim) { + var type = CSSPROPS[info.type]; + if (type === false) + return apf.tween; + info.type = type || info.type; + if (isTransform) { + if (!info.subType) + info.subType = SCALE; + info.type = apf.supportCSSAnim; + } + + var transform = (isTransform) + ? modules[TRANSVAL + (info.subType || SCALE)] + : null; + + oHtml.style[info.type] = isTransform + ? transform(info.from) + : info.from + (needsPix[info.type] ? PX : ""); + $setTimeout(function() { + oHtml.style[info.type] = isTransform + ? transform(info.to) + : info.to + (needsPix[info.type] ? PX : ""); + oHtml.offsetTop; //force style recalc + oHtml.style[apf.cssPrefix + "Transition"] = info.type + " " + ((info.steps + * info.interval) / 1000) + "s " + + CSSTIMING[info.anim || 0]; + var f = function() { + if (info.onfinish) + info.onfinish(oHtml, info.userdata); + oHtml.style[apf.cssPrefix + "Transition"] = ""; + oHtml.removeEventListener(apf.cssAnimEvent, f); + }; + oHtml.addEventListener(apf.cssAnimEvent, f); + }); + return apf.tween; + } + + if (info.control) { + info.control.state = apf.tween.RUNNING; + info.control.stop = function(){ + info.control.state = apf.tween.STOPPING; + clearQueue(oHtml); + if (info.onstop) + info.onstop(oHtml, info.userdata); + } + } + + var steps = info.color + ? calcColorSteps(info.anim, info.from, info.to, info.steps) + : calcSteps(info.anim, parseFloat(info.from), parseFloat(info.to), info.steps), + stepFunction = function(step){ + if (info.control && info.control.state) { + info.control.state = apf.tween.STOPPED; + return; + } + + current = info; + + if (info.onbeforeeach + && info.onbeforeeach(oHtml, info.userdata) === false) + return; + + try { + info.method(oHtml, steps[step], info); + } + catch (e) {} + + if (info.oneach) + info.oneach(oHtml, info.userdata); + + if (step < info.steps) + return $setTimeout(function(){stepFunction(step + 1)}, info.interval); + + current = null; + if (info.control) + info.control.state = apf.tween.STOPPED; + if (info.onfinish) + info.onfinish(oHtml, info.userdata); + + nextQueue(oHtml); + }; + + if (info.type.indexOf("scroll") > -1) + purgeQueue(oHtml); + setQueue(oHtml, stepFunction); + + return apf.tween; + }, + + /** + * Tweens multiple properties of a single element or html element from a + * start to an end value. + * Example: + * Animating both the left and width at the same time. + * + * apf.tween.multi(myDiv, { + * anim : apf.tween.EASEIN + * tweens : [{ + * type : "left", + * from : 10, + * to : 100, + * }, + * { + * type : "width", + * from : 100, + * to : 400, + * }] + * }); + * + * Example: + * Multiple animations can be run after eachother + * by calling this function multiple times. + * + * apf.tween.multi(myDiv, options).multi(myDiv2, options2); + * + * @param {Element} oHtml the object to animate. + * @param {Object} info the settings of the animation. + * Properties: + * {Number} [steps] the number of steps to divide the tween in + * {Number} [interval] the time between each step + * {Number} [anim] the distribution of change between the step over + * the entire animation + * {Function} [onfinish] a function that is called at the end of the animation + * {Function} [oneach] a function that is called at each step of the animation + * {HTMLElement} [oHtml] another html element to animate. + * {Object} [control] an object that can stop the animation at any point + * Properties: + * {Boolean} stop whether the animation should stop. + * {Array} [tweens] a collection of simple objects specifying the single + * value animations that are to be executed simultaneously. + * (for the properties of these single tweens see the + * single tween method). + */ + multi = function(oHtml, info){ + info = apf.extend({steps: 10, interval: 5, anim: apf.tween.linear, control: {}}, info); + info.steps = Math.ceil(info.steps * apf.animSteps); + info.interval = Math.ceil(info.interval * apf.animInterval); + + if (oHtml.nodeFunc > 100) { + info.$int = oHtml.$int; + oHtml = oHtml.$ext; + } + + var animCSS, isTransform, + useCSSAnim = apf.supportCSSAnim && apf.supportCSSTransition, + hasCSSAnims = false, + cssDuration = ((info.steps * info.interval) / 1000), + cssAnim = CSSTIMING[info.anim || 0], + steps = [], + stepsTo = [], + i = 0, + l = info.tweens.length; + + for (; i < l; i++) { + var data = info.tweens[i]; + + if (data.oHtml && data.oHtml.nodeFunc > 100) { + data.$int = data.oHtml.$int; + data.oHtml = data.oHtml.$ext; + } + + animCSS = (useCSSAnim && CSSPROPS[data.type]); + isTransform = (data.type == TRANSFORM); + if (isTransform) { + if (!data.subType) + data.subType = SCALE; + data.type = apf.supportCSSAnim; + } + + data.method = animCSS + ? data.type + : isTransform + ? modules[TRANSFORM + (data.subType)] + : modules[data.type] + ? modules[data.type] + : (data.needsPx = needsPix[data.type] || false) + ? modules.htmlcss + : modules.htmlcss; + + + + + if (animCSS) { + var type = isTransform ? data.type : CSSPROPS[data.type]; + data.type = type || data.type; + var transform = modules[TRANSVAL + (data.subType)] + + oHtml.style[data.type] = isTransform + ? transform(data.from) + : data.from + (needsPix[data.type] ? PX : ""); + stepsTo.push([data.type, isTransform + ? transform(data.to) + : data.to + (needsPix[data.type] ? PX : "")]); + steps.push(data.type + " " + cssDuration + "s " + cssAnim + " 0"); + + hasCSSAnims = true; + } + else { + steps.push(data.color + ? calcColorSteps(info.anim, data.from, data.to, info.steps) + : calcSteps(info.anim, parseFloat(data.from), parseFloat(data.to), info.steps)); + } + } + + if (hasCSSAnims) { + oHtml.style[apf.cssPrefix + "Transition"] = steps.join(","); + oHtml.offsetTop; //force style recalc + var count = 0, + func = function() { + count++; + if (count == stepsTo.length) { + if (info.onfinish) + info.onfinish(oHtml, info.userdata); + oHtml.style[apf.cssPrefix + "Transition"] = ""; + oHtml.removeEventListener(apf.cssAnimEvent, func); + } + }; + oHtml.addEventListener(apf.cssAnimEvent, func, false); + for (var k = 0, j = stepsTo.length; k < j; k++) + oHtml.style[stepsTo[k][0]] = stepsTo[k][1]; + return apf.tween; + } + + if (info.control) { + info.control.state = apf.tween.RUNNING; + info.control.stop = function(){ + if (info.control.state == apf.tween.STOPPED) + return; + + info.control.state = apf.tween.STOPPING; + clearQueue(oHtml); + if (info.onstop) + info.onstop(oHtml, info.userdata); + } + } + + var tweens = info.tweens, + stepFunction = function(step){ + if (info.control && info.control.state) { + info.control.state = apf.tween.STOPPED; + return; + } + + current = info; + + try { + for (var i = 0; i < steps.length; i++) { + tweens[i].method(tweens[i].oHtml || oHtml, + steps[i][step], tweens[i]); + } + } catch (e) {} + + if (info.oneach) + info.oneach(oHtml, info.userdata); + + if (step < info.steps) + return $setTimeout(function(){stepFunction(step + 1)}, info.interval); + + current = null; + if (info.control) + info.control.state = apf.tween.STOPPED; + if (info.onfinish) + info.onfinish(oHtml, info.userdata); + + nextQueue(oHtml); + }; + + setQueue(oHtml, stepFunction); + + return apf.tween; + }, + + /** + * Tweens an element or html element from it's current state to a css class. + * Example: + * Multiple animations can be run after eachother by calling this function + * multiple times. + * + * apf.tween.css(myDiv, 'class1').multi(myDiv2, 'class2'); + * + * @param {Element} oHtml the object to animate. + * @param {String} className the classname that defines the css properties to be set or removed. + * @param {Object} info the settings of the animation. + * Properties: + * {Number} [steps] the number of steps to divide the tween in + * {Number} [interval] the time between each step + * {Number} [anim] the distribution of change between the step over the entire animation + * {Function} [onfinish] a function that is called at the end of the animation + * {Function} [oneach] a function that is called at each step of the animation + * {Object} [control] an object that can stop the animation at any point + * Properties: + * {Boolean} stop whether the animation should stop. + * @param {Boolean} remove whether the class is set or removed from the element or html element + */ + css = function(oHtml, className, info, remove){ + (info = info || {}).tweens = []; + + if (oHtml.nodeFunc > 100) + oHtml = oHtml.$ext; + + if (remove) + apf.setStyleClass(oHtml, "", [className]); + + var resetAnim = function(remove, callback){ + if (remove) + apf.setStyleClass(oHtml, "", [className]); + else + apf.setStyleClass(oHtml, className); + + //Reset CSS values + for (var i = 0; i < info.tweens.length; i++){ + if (info.tweens[i].type == "filter") + continue; + + oHtml.style[info.tweens[i].type] = ""; + } + + if (callback) + callback.apply(this, arguments); + } + + var onfinish = info.onfinish, + onstop = info.onstop; + info.onfinish = function(){resetAnim(remove, onfinish);} + info.onstop = function(){resetAnim(!remove, onstop);} + + var result, newvalue, curvalue, j, isColor, style, rules, i, + tweens = {}; + for (i = 0; i < document.styleSheets.length; i++) { + rules = document.styleSheets[i][apf.styleSheetRules]; + for (j = rules.length - 1; j >= 0; j--) { + var rule = rules[j]; + + if (!rule.style || !rule.selectorText.match("\." + className + "$")) + continue; + + for (style in rule.style) { + if (!rule.style[style] || cssProps.indexOf("|" + style + "|") == -1) + continue; + + if (style == "filter") { + if (!rule.style[style].match(/opacity\=([\d\.]+)/)) + continue; + newvalue = RegExp.$1; + + result = (apf.getStyleRecur(oHtml, style) || "") + .match(/opacity\=([\d\.]+)/); + curvalue = result ? RegExp.$1 : 100; + isColor = false; + + if (newvalue == curvalue) { + if (remove) curvalue = 100; + else newvalue = 100; + } + } + else { + newvalue = remove && oHtml.style[style] || rule.style[style]; + if (remove) oHtml.style[style] = ""; + curvalue = apf.getStyleRecur(oHtml, style); + isColor = style.match(/color/i) ? true : false; + } + + tweens[style] = { + type : style, + from : (isColor ? String : parseFloat)(remove + ? newvalue + : curvalue), + to : (isColor ? String : parseFloat)(remove + ? curvalue + : newvalue), + color : isColor, + needsPx : needsPix[style.toLowerCase()] || false + }; + } + } + } + + for (var prop in tweens) + info.tweens.push(tweens[prop]); + + if (remove) + apf.setStyleClass(oHtml, className); + + return multi(oHtml, info); + }, + + cssRemove = function(oHtml, className, info){ + css(oHtml, className, info, true); + }, + + needsPix = { + "left" : true, + "top" : true, + "bottom" : true, + "right" : true, + "width" : true, + "height" : true, + "fontSize" : true, + "lineHeight" : true, + "textIndent" : true, + "marginLeft" : true, + "marginTop" : true, + "marginRight" : true, + "marginBottom": true + }, + + cssProps = "|backgroundColor|backgroundPosition|color|width|filter" + + "|height|left|top|bottom|right|fontSize" + + "|letterSpacing|lineHeight|textIndent|opacity" + + "|paddingLeft|paddingTop|paddingRight|paddingBottom" + + "|borderLeftWidth|borderTopWidth|borderRightWidth|borderBottomWidth" + + "|borderLeftColor|borderTopColor|borderRightColor|borderBottomColor" + + "|marginLeft|marginTop|marginRight|marginBottom" + + "|transform|", // transforms are special and get special treatment + cssTransforms = "|scale|rotate|"; + +return { + single: single, + multi: multi, + css: css, + cssRemove: cssRemove, + clearQueue: clearQueue, + addModule: function(name, func, force) { + if (typeof name != "string" || typeof func != "function" || (modules[name] && !force)) + return this; + modules[name] = func; + return this; + }, + /** Linear tweening method */ + NORMAL: 0, + /** Ease-in tweening method */ + EASEIN: 1, + /** Ease-out tweening method */ + EASEOUT: 2, + + RUNNING: 0, + STOPPING: 1, + STOPPED: 2, + + calcColorSteps: calcColorSteps, + + linear: function(t, x_min, dx) { + return dx * t + x_min; + }, + easeInQuad: function(t, x_min, dx) { + return dx * __pow(t, 2) + x_min; + }, + easeOutQuad: function(t, x_min, dx) { + return -dx * t * (t - 2) + x_min; + }, + easeInOutQuad: function(t, x_min, dx) { + if ((t /= .5) < 1) + return dx / 2 * t * t + x_min; + return -dx / 2 * ((--t) * (t - 2) - 1) + x_min; + }, + easeInCubic: function(t, x_min, dx) { + return dx * __pow(t, 3) + x_min; + }, + easeOutCubic: function(t, x_min, dx) { + return dx * (__pow(t - 1, 3) + 1) + x_min; + }, + easeInOutCubic: function(t, x_min, dx) { + if ((t /= .5) < 1) + return dx / 2 * __pow(t, 3) + x_min; + return dx / 2 * (__pow(t - 2, 3) + 2) + x_min; + }, + easeInQuart: function(t, x_min, dx) { + return dx * __pow(t, 4) + x_min; + }, + easeOutQuart: function(t, x_min, dx) { + return -dx * (__pow(t - 1, 4) - 1) + x_min; + }, + easeInOutQuart: function(t, x_min, dx) { + if ((t /= .5) < 1) + return dx / 2 * __pow(t, 4) + x_min; + return -dx / 2 * (__pow(t - 2, 4) - 2) + x_min; + }, + easeInQuint: function(t, x_min, dx) { + return dx * __pow(t, 5) + x_min; + }, + easeOutQuint: function(t, x_min, dx) { + return dx * (__pow(t - 1, 5) + 1) + x_min; + }, + easeInOutQuint: function(t, x_min, dx) { + if ((t /= .5) < 1) + return dx / 2 * __pow(t, 5) + x_min; + return dx / 2 * (__pow(t - 2, 5) + 2) + x_min; + }, + easeInSine: function(t, x_min, dx) { + return -dx * Math.cos(t * (Math.PI / 2)) + dx + x_min; + }, + easeOutSine: function(t, x_min, dx) { + return dx * Math.sin(t * (Math.PI / 2)) + x_min; + }, + easeInOutSine: function(t, x_min, dx) { + return -dx / 2 * (Math.cos(Math.PI * t) - 1) + x_min; + }, + easeInExpo: function(t, x_min, dx) { + return (t == 0) ? x_min : dx * __pow(2, 10 * (t - 1)) + x_min; + }, + easeOutExpo: function(t, x_min, dx) { + return (t == 1) ? x_min + dx : dx * (-__pow(2, -10 * t) + 1) + x_min; + }, + easeInOutExpo: function(t, x_min, dx) { + if (t == 0) + return x_min; + if (t == 1) + return x_min + dx; + if ((t /= .5) < 1) + return dx / 2 * __pow(2, 10 * (t - 1)) + x_min; + return dx / 2 * (-__pow(2, -10 * --t) + 2) + x_min; + }, + easeInCirc: function(t, x_min, dx) { + return -dx * (Math.sqrt(1 - t * t) - 1) + x_min; + }, + easeOutCirc: function(t, x_min, dx) { + return dx * Math.sqrt(1 - (t -= 1) * t) + x_min; + }, + easeInOutCirc: function(t, x_min, dx) { + if ((t /= .5) < 1) + return -dx / 2 * (Math.sqrt(1 - t * t) - 1) + x_min; + return dx / 2 * (Math.sqrt(1 - (t -= 2) * t) + 1) + x_min; + }, + easeInElastic: function(t, x_min, dx) { + var s = 1.70158, + p = .3, + a = dx; + if (t == 0) + return x_min; + if (t == 1) + return x_min + dx; + if (!a || a < Math.abs(dx)) { + a = dx; + s = p / 4; + } + else + s = p / (2 * Math.PI) * Math.asin (dx / a); + return -(a * __pow(2, 10 * (t -= 1)) * Math.sin((t * 1 - s) * (2 * Math.PI) / p)) + x_min; + }, + easeOutElastic: function(t, x_min, dx) { + var s = 1.70158, + p = .3, + a = dx; + if (t == 0) + return x_min; + if (t == 1) + return x_min + dx; + if (a < Math.abs(dx)) { + a = dx; + s = p / 4; + } + else { + s = p / (2 * Math.PI) * Math.asin(dx / a); + } + return a * __pow(2, -10 * t) * Math.sin((t - s) * (2 * Math.PI) / p) + dx + x_min; + }, + easeInOutElastic: function(t, x_min, dx) { + var s = 1.70158, + p = 0, + a = dx; + if (t == 0) + return x_min; + if ((t / 2) == 2) + return x_min + dx; + if (!p) + p = .3 * 1.5; + if (a < Math.abs(dx)) { + a = dx; + s = p / 4; + } + else { + s = p / (2 * Math.PI) * Math.asin(dx / a); + } + if (t < 1) + return -.5 * (a * __pow(2, 10 * (t -= 1)) * Math.sin((t - s) * (2 * Math.PI) / p)) + x_min; + return a * __pow(2, -10 * (t -= 1)) * Math.sin((t - s) * (2 * Math.PI) / p) * .5 + dx + x_min; + }, + easeInBack: function(t, x_min, dx) { + var s = 1.70158; + return dx * __pow(t, 2) * ((s + 1) * t - s) + x_min; + }, + easeOutBack: function(t, x_min, dx) { + var s = 1.70158; + return dx * ((t -= 1) * t * ((s + 1) * t + s) + 1) + x_min; + }, + easeInOutBack: function(t, x_min, dx) { + var s = 1.70158; + if ((t / 2) < 1) + return dx / 2 * (t * t * (((s *= (1.525)) + 1) * t - s)) + x_min; + return dx / 2 * ((t -= 2) * t * (((s *= (1.525)) + 1) * t + s) + 2) + x_min; + }, + easeInBounce: function(t, x_min, dx) { + return dx - apf.tween.easeOutBounce(1 - t, 0, dx) + x_min; + }, + easeOutBounce: function(t, x_min, dx) { + if (t < (1 / 2.75)) + return dx * (7.5625 * t * t) + x_min; + else if (t < (2 / 2.75)) + return dx * (7.5625 * (t -= (1.5 / 2.75)) * t + .75) + x_min; + else if (t < (2.5 / 2.75)) + return dx * (7.5625 * (t -= (2.25 / 2.75)) * t + .9375) + x_min; + else + return dx * (7.5625 * (t -= (2.625 / 2.75)) * t + .984375) + x_min; + }, + easeInOutBounce: function(t, x_min, dx) { + if (t < 1 / 2) + return apf.tween.easeInBounce(t * 2, 0, dx) * .5 + x_min; + return apf.tween.easeOutBounce(t * 2 - 1, 0, dx) * .5 + dx * .5 + x_min; + } +}; + +})(apf); + + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/core/lib/uirecorder.js)SIZE(397)TIME(Tue, 06 Dec 2011 16:22:54 GMT)*/ + + +/** + * Provides a way to record user actions, store them and play them back. + * @experimental + */ +apf.uirecorder = { + $inited : false, + isRecording : false, + isPlaying : false, + isPaused : false, + captureDetails : false, + $o3 : null, + + setTimeout : self.setTimeout +} + + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/core/lib/vector.js)SIZE(46289)TIME(Fri, 14 Oct 2011 08:01:44 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/core/lib/xmldb.js)SIZE(39996)TIME(Tue, 13 Dec 2011 13:33:58 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + + +/** + * The xml database object provides local storage for xml data. This object + * routes all changes to the xml data to the data bound objects. It further + * provides utility functions for xml handling. + * + * @constructor + * @apfclass + * + * @author Ruben Daniels (ruben AT ajax DOT org) + * @version %I%, %G% + * @since 0.8 + * + * @default_private + */ +apf.xmldb = new (function(){ + this.xmlDocTag = "a_doc"; + this.xmlIdTag = "a_id"; + this.xmlListenTag = "a_listen"; + this.htmlIdTag = "id"; + this.disableRDB = false; + + this.$xmlDocLut = []; + this.$nodeCount = {}; + + var cleanRE = /(?:a_doc|a_id|a_listen|a_loaded)=(?:"|')[^'"]+(?:"|')/g, + whiteRE = />[\s\n\r\t]+ -1) + this.notifyQueued(); //empty queue + + var listen, uId, uIds, i, j, hash, info, amlNode, runTimer, found, done = {}; + while (loopNode && loopNode.nodeType == 1) { + //Get List of Node this.$listeners ID's + listen = loopNode.getAttribute(this.xmlListenTag); + + if (listen) { + uIds = listen.split(";"); + + for (i = 0; i < uIds.length; i++) { + uId = uIds[i]; + if (!uId || done[uId]) continue; + done[uId] = true; + + //Property support + /*if (uId.charAt(0) == "p") { + uId = uId.split("|"); + + //@todo apf3.0 should this be exactly like in class.js? + //@todo optimize this to check the async flag: parsed[3] & 4 + + amlNode = apf.all[uId[1]]; //It's possible the aml node dissapeared in this loop. + if (amlNode) { + var model = apf.all[uId[3]]; + var xpath = model.$propBinds[uId[1]][uId[2]].root; + + amlNode.$execProperty(uId[2], xpath + ? model.data.selectSingleNode(xpath) + : model.data); + } + continue; + }*/ + + hash = notifyQueue[uId]; + if (!hash) + notifyQueue[uId] = hash = []; + + // Filtering + if (!apf.isO3 && "|update|attribute|text|".indexOf("|" + action + "|") > -1) { + found = false; + for (j = 0; j < hash.length; j++) { + if (hash[j] && xmlNode == hash[j][1] + && "|update|attribute|text|" + .indexOf("|" + hash[j][0] + "|") > -1) { + hash[j] = null; + found = true; + continue; + } + } + + hash.push([action, xmlNode, loopNode, undoObj, oParent]); + runTimer = true; + continue; + } + + //!this.delayUpdate && <- that doesnt work because of information that is destroyed + if (apf.isO3 || "|remove|move-away|move|add|".indexOf("|" + action + "|") > -1) { + if (this.$listeners[uId]) { + this.$listeners[uId]([action, xmlNode, + loopNode, undoObj, oParent]); + } + /*amlNode = apf.all[uId]; + if (amlNode) + amlNode.$xmlUpdate(action, xmlNode, + loopNode, undoObj, oParent);*/ + } + else { + hash.push([action, xmlNode, loopNode, undoObj, oParent]); + runTimer = true; + } + } + } + + //Go one level up + loopNode = loopNode.parentNode || nextloop; + if (loopNode == nextloop) + nextloop = null; + } + + if (undoObj && !this.delayUpdate) { + //Ok this was an action let's not delay execution + apf.xmldb.notifyQueued(); + } + else if (runTimer) { + clearTimeout(notifyTimer); + //@todo find a better solution for this (at the end of a event stack unroll) + this.$hasQueue = true; + notifyTimer = apf.setZeroTimeout(function(){ + //this.$hasQueue = true; + apf.xmldb.notifyQueued(); + }); + } + }; + + /** + * @todo in actiontracker - add stack auto purging + * - when undo item is purged which was a removed, remove cache item + * @todo shouldn't the removeNode method remove all this.$listeners? + * @todo rename to processQueue + * @private + */ + this.notifyQueued = function(){ + this.$hasQueue = false; + + var myQueue = notifyQueue; + notifyQueue = {}; + + clearTimeout(notifyTimer); + for (var uId in myQueue) { + if (!uId) continue; + + var q = myQueue[uId]; + var func = this.$listeners[uId]; + //!amlNode || + if (!q || !func) + continue; + + //Run queue items + for (var i = 0; i < q.length; i++) { + if (!q[i]) + continue; + + //Update xml data + //amlNode.$xmlUpdate.apply(amlNode, q[i]); + func(q[i]); + } + } + + + } + + /** + * @private + */ + this.notifyListeners = function(xmlNode){ + //This should be done recursive + var listen = xmlNode.getAttribute(apf.xmldb.xmlListenTag); + if (listen) { + listen = listen.split(";"); + for (var j = 0; j < listen.length; j++) { + apf.all[listen[j]].$xmlUpdate("synchronize", xmlNode, xmlNode); + //load(xmlNode); + } + } + }; + + + + /** + * @private + */ + this.copyConnections = function(fromNode, toNode){ + //This should copy recursive + try { + toNode.setAttribute(this.xmlListenTag, fromNode.getAttribute(this.xmlListenTag)); + } + catch (e) {} + try { + toNode.setAttribute(this.xmlIdTag, fromNode.getAttribute(this.xmlIdTag)); + } + catch (e) {} + }; + + /** + * @private + */ + this.cleanXml = function(xml) { + if (typeof xml != "string") + return xml; + return xml.replace(cleanRE, "").replace(whiteRE, "><"); + }; + + /** + * @private + */ + this.cleanNode = function(xmlNode){ + try { + var i, nodes = xmlNode.selectNodes("descendant-or-self::node()[@" + this.xmlListenTag + "]"); + for (i = nodes.length - 1; i >= 0; i--) + nodes[i].removeAttribute(this.xmlListenTag); + nodes = xmlNode.selectNodes("descendant-or-self::node()[@" + this.xmlIdTag + "]"); + for (i = nodes.length - 1; i >= 0; i--) + nodes[i].removeAttribute(this.xmlIdTag); + nodes = xmlNode.selectNodes("descendant-or-self::node()[@" + this.xmlDocTag + "]"); + for (i = nodes.length - 1; i >= 0; i--) + nodes[i].removeAttribute(this.xmlDocTag); + nodes = xmlNode.selectNodes("descendant-or-self::node()[@a_loaded]"); + for (i = nodes.length - 1; i >= 0; i--) + nodes[i].removeAttribute("a_loaded"); + } + catch (e) {} + + return xmlNode; + }; + + /** + * Returns a copy of the passed {@link term.datanode data node}. Bound + * data nodes contain special attributes to track them. These attributes + * are removed from the copied node when using this method. + * + * @param {XMLElement} xmlNode the {@link term.datanode data node} to copy. + * @return {XMLElement} the copy of the {@link term.datanode data node}. + */ + this.copy = + this.getCleanCopy = + apf.getCleanCopy = function(xmlNode){ + return apf.xmldb.cleanNode(xmlNode.cloneNode(true)); + }; + + /** + * Unbind all APF Elements from a certain Form + * @private + */ + this.unbind = function(frm){ + //Loop through objects of all apf + for (var lookup = {}, i = 0; i < frm.apf.all.length; i++) + if (frm.apf.all[i] && frm.apf.all[i].unloadBindings) + lookup[frm.apf.all[i].unloadBindings()] = true; + + //Remove Listen Nodes + for (var k = 0; k < this.$xmlDocLut.length; k++) { + + if (!this.$xmlDocLut[k]) continue; + + + var Nodes = this.$xmlDocLut[k].selectNodes("//self::node()[@" + + this.xmlListenTag + "]"); + if (!Nodes) continue; + + //Loop through Nodes and rebuild listen array + for (var i = 0; i < Nodes.length; i++) { + var listen = Nodes[i].getAttribute(this.xmlListenTag).split(";"); + for (var nListen = [], j = 0; j < listen.length; j++) + if (!lookup[listen[j]]) + nListen.push(listen[j]); + + //Optimization?? + if (nListen.length != listen.length) + Nodes[i].setAttribute(this.xmlListenTag, nListen.join(";")); + } + } + }; + + /** + * @private + * @todo xml doc leakage + */ + this.getXmlDocId = function(xmlNode, model){ + var docEl = xmlNode.ownerDocument.documentElement; + if (!apf.isChildOf(docEl, xmlNode)) + docEl = xmlNode; + + var docId = (docEl || xmlNode).getAttribute(this.xmlDocTag) + || this.$xmlDocLut.indexOf(docEl || xmlNode.ownerDocument || xmlNode); + + if (model && apf.nameserver.get("model", docId) != model) { + docId = null; + docEl = xmlNode; + } + + if (!docId || docId == -1) { + docId = this.$xmlDocLut.push(docEl || xmlNode.ownerDocument || xmlNode) - 1; + if (docEl) + docEl.setAttribute(this.xmlDocTag, String(docId)); + } + + if (model) + apf.nameserver.register("model", docId, model); + + + return docId; + }; +}); + + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/core/lib/draw/canvas.js)SIZE(21818)TIME(Fri, 14 Oct 2011 08:01:44 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/core/lib/draw/chartdraw.js)SIZE(47182)TIME(Fri, 14 Oct 2011 08:01:44 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/core/lib/draw/vml.js)SIZE(20284)TIME(Fri, 14 Oct 2011 08:01:44 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/core/lib/storage/air.file.js)SIZE(10053)TIME(Fri, 14 Oct 2011 08:01:44 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/core/lib/storage/air.js)SIZE(9669)TIME(Fri, 14 Oct 2011 08:01:44 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/core/lib/storage/air.sql.js)SIZE(11835)TIME(Fri, 14 Oct 2011 08:01:44 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/core/lib/storage/cookie.js)SIZE(10313)TIME(Fri, 14 Oct 2011 08:01:44 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/core/lib/storage/flash.js)SIZE(15458)TIME(Fri, 14 Oct 2011 08:01:44 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/core/lib/storage/gears.js)SIZE(12312)TIME(Fri, 14 Oct 2011 08:01:44 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/core/lib/storage/html5.js)SIZE(8228)TIME(Fri, 14 Oct 2011 08:01:44 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/core/lib/storage/memory.js)SIZE(10208)TIME(Fri, 14 Oct 2011 08:01:44 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/core/lib/teleport/http.js)SIZE(38120)TIME(Tue, 06 Dec 2011 16:22:54 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + + +/** + * This object does what is commonly known as Ajax, it Asynchronously + * communicates using Javascript And in most + * cases it sends or receives Xml. It allows for easy http + * communication from within the browser. This object provides + * {@link teleport.http.method.savecache caching} on top of + * the browser's cache. This enables you to optimize your application, because + * this can be set on a per call basis. + * Example: + * Retrieving content over http synchronously: + * + * var http = new apf.http(); + * var data = http.get("http://www.example.com/mydata.jsp", {async: false}); + * alert(data); + * + * Example: + * Retrieving content over http asynchronously: + * + * var http = new apf.http(); + * http.get("http://www.example.com/mydata.jsp", { + * callback: function(data, state, extra){ + * if (state != apf.SUCCESS) + * return alert('an error has occurred'); + * + * alert(data); + * } + * }); + * + * Example: + * Async http request with retry. + * + * var http = new apf.http(); + * http.get("http://www.example.com/mydata.jsp", { + * callback: function(data, state, extra){ + * if (state != apf.SUCCESS) { + * var oError = new Error(apf.formatErrorString(0, null, + * "While loading data", "Could not load data\n" + extra.message)); + * + * if (extra.tpModule.retryTimeout(extra, state, null, oError) === true) + * return true; + * + * throw oError; + * } + * + * alert(data); + * } + * }); + * + * + * @event error Fires when a communication error occurs. + * bubbles: yes + * cancelable: Prevents a communication error to be thrown. + * object: + * {Error} error the error object that is thrown when the event + * callback doesn't return false. + * {Number} state the state of the call + * Possible values: + * apf.SUCCESS the request was successfull + * apf.TIMEOUT the request has timed out. + * apf.ERROR an error has occurred while making the request. + * apf.OFFLINE the request was made while the application was offline. + * {mixed} userdata data that the caller wanted to be available in + * the callback of the http request. + * {XMLHttpRequest} http the object that executed the actual http request. + * {String} url the url that was requested. + * {Http} tpModule the teleport module that is making the request. + * {Number} id the id of the request. + * {String} message the error message. + * + * @constructor + * @define http + * @addnode teleport + * @default_private + * + * @author Ruben Daniels (ruben AT ajax DOT org) + * @version %I%, %G% + * @since 0.4 + */ +apf.http = function(){ + this.queue = [null]; + this.callbacks = {}; + this.cache = {}; + + /** + * Sets the timeout of http requests in milliseconds. Default is 10000ms (10s). + */ + this.timeout = this.timeout || 10000; //default 10 seconds + + /** + * Sets whether this element routes traffic through a server proxy. + * Remarks: + * This can also be set on a per call basis. See {@link teleport.http.method.get}. + */ + this.autoroute = this.autoroute || false; + + /** + * String specifying the url to the route script. + * Remarks: + * The route script will receive the route information in 3 extra headers: + * X-Route-Request - Containing the destination url.
+ * X-Proxy-Request - Containing the destination url.
+ * X-Compress-Response - Set to 'gzip'.
+ */ + this["route-server"] = this["route-server"] || null; + + if (!this.$uniqueId) + this.$uniqueId = apf.all.push(this) - 1; + + this.toString = this.toString || function(){ + return "[Ajax.org Teleport Component : (HTTP)]"; + }; + + + + /** + * Makes an http request that receives xml + * @param {String} url the url that is accessed. + * @param {Object} options the options for the http request + * Properties: + * {Boolean} async whether the request is sent asynchronously. Defaults to true. + * {mixed} userdata custom data that is available to the callback function. + * {String} method the request method (POST|GET|PUT|DELETE). Defaults to GET. + * {Boolean} nocache whether browser caching is prevented. + * {String} data the data sent in the body of the message. + * {Boolean} autoroute whether the request can fallback to a server proxy. + * {Boolean} caching whether the request should use internal caching. + * {Boolean} ignoreOffline whether to ignore offline catching. + * {Function} callback the handler that gets called whenever the + * request completes succesfully or with an error, + * or when the request times out. + */ + this.getXml = function(url, callback, options){ + if (!options) options = {}; + options.useXML = true; + options.callback = callback; + return this.get(url, options); + }; + + this.getJSON = function(url, callback, options){ + if (!options) options = {}; + options.callback = callback; + options.useJSON = true; + return this.get(url, options); + }; + + /** + * Makes an http request. + * @param {String} url the url that is accessed. + * @param {Object} options the options for the http request + * Properties: + * {Boolean} async whether the request is sent asynchronously. Defaults to true. + * {mixed} userdata custom data that is available to the callback function. + * {String} method the request method (POST|GET|PUT|DELETE). Defaults to GET. + * {Boolean} nocache whether browser caching is prevented. + * {String} data the data sent in the body of the message. + * {Boolean} useXML whether the result should be interpreted as xml. + * {Boolean} autoroute whether the request can fallback to a server proxy. + * {Boolean} caching whether the request should use internal caching. + * {Boolean} ignoreOffline whether to ignore offline catching. + * {String} contentType the mime type of the message + * {Function} callback the handler that gets called whenever the + * request completes succesfully or with an error, + * or when the request times out. + */ + this.get = this.$get = function(url, options){ + if (!options) + options = {}; + + var _self = this; + var id = options.id; + + + + if (apf.uirecorder && apf.uirecorder.captureDetails) { + if (apf.uirecorder.isRecording || apf.uirecorder.isTesting) {// only capture events when recording apf.uirecorder.isLoaded + apf.uirecorder.capture.trackHttpCall(this, url, options); + } + } + + + var binary = apf.hasXhrBinary && options.binary; + var async = options.async = (options.async || binary + || typeof options.async == "undefined" || apf.isOpera || false); + + + if (apf.isWebkit) + url = apf.html_entity_decode(url); + + + var data = options.data || ""; + + if (apf.isNot(id)) { + + var http = apf.getHttpReq(); + + id = this.queue.push({ + http : http, + url : url, + callback : options.callback, + retries : 0, + options : options + }) - 1; + + + } + else { + var http = this.queue[id].http; + + + http.abort(); + } + + if (async) { + + { + http.onreadystatechange = function(){ + if (!_self.queue[id] || http.readyState != 4) + return; + if (async && arguments.callee.caller) + $setTimeout(function(){_self.receive(id)}); + else + _self.receive(id); + } + } + } + + var autoroute = this.autoroute && apf.isOpera + ? true //Bug in opera + : (options.autoroute || this.shouldAutoroute), + httpUrl = autoroute ? this["route-server"] : url; + + + var headers = []; + + function setRequestHeader(name, value){ + + http.setRequestHeader(name, value); + } + + var errorFound = false; + try { + if (options.nocache) + httpUrl = apf.getNoCacheUrl(httpUrl); + + + if (apf.config.queryAppend) { + httpUrl += (httpUrl.indexOf("?") == -1 ? "?" : "&") + + apf.config.queryAppend; + } + + + // experimental for Firefox Cross Domain problem + // http://ubiquity-xforms.googlecode.com/svn/wiki/CrossDomainSubmissionDeployment.wiki + + //Currently we don't support html5 cross domain access + if (apf.hasHtml5XDomain + && httpUrl.match(/^http:\/\//) + && !new apf.url(httpUrl).isSameLocation()) { + throw new Error(apf.formatErrorString(0, + this, "Communication error", "Url: " + httpUrl + + "\nReason: Same origin policy in effect")); + } + + //end experimental + + http.open(this.method || options.method || "GET", httpUrl, async); + + if (options.username) { + setRequestHeader("Authorization", "Basic " + + apf.crypto.Base64.encode(options.username + ":" + options.password)) + } + + //@todo OPERA ERROR's here... on retry [is this still applicable?] + setRequestHeader("X-Requested-With", "XMLHttpRequest"); + if (!options.headers || !options.headers["Content-type"]) + setRequestHeader("Content-type", options.contentType || this.contentType + || (this.useXML || options.useXML ? "text/xml" : "text/plain")); + + if (autoroute) { + setRequestHeader("X-Route-Request", url); + setRequestHeader("X-Proxy-Request", url); + setRequestHeader("X-Compress-Response", "gzip"); + } + + if (binary) { + setRequestHeader("Cache-Control", "no-cache"); + setRequestHeader("X-File-Name", binary.filename); + setRequestHeader("X-File-Size", binary.filesize); + setRequestHeader("Content-Type", "application/octet-stream"); + } + } + catch (e) { + errorFound = e.message; + } + + if (errorFound) { + var useOtherXH = false; + + + + // Retry request by routing it + if (!useOtherXH && this.autoroute && !autoroute) { + + + this.shouldAutoroute = true; + + options.autoroute = true; + return this.get(url, options); + } + + if (!useOtherXH) { + //Routing didn't work either... Throwing error + var noClear = options.callback ? options.callback(null, apf.ERROR, { + userdata: options.userdata, + http : http, + url : url, + tpModule: this, + id : id, + message : "Permission denied accessing remote resource: " + + url + "\nMessage: " + errorFound + }) : false; + if (!noClear) + this.clearQueueItem(id); + + return; + } + } + + if (this.$headerHook) + this.$headerHook(http); + + //Set request headers + if (options.headers) { + for (var name in options.headers) + setRequestHeader(name, options.headers[name]); + } + + + + function handleError(){ + var msg = self.navigator && self.navigator.onLine + ? "File or Resource not available " + url + : "Browser is currently working offline"; + + + + var state = self.navigator && navigator.onLine + ? apf.ERROR + : apf.TIMEOUT; + + // File not found + var noClear = options.callback ? options.callback(null, state, { + userdata : options.userdata, + http : http, + url : url, + tpModule : _self, + id : id, + message : msg + }) : false; + if (!noClear) + _self.clearQueueItem(id); + } + + function send(isLocal){ + var hasError; + + if (apf.isIE && isLocal) { //When local IE calls onreadystate immediately + var oldWinOnerror = window.onerror; + window.onerror = function(){ + if (arguments.caller && arguments.caller.callee == send) { + window.onerror = oldWinOnerror; + //_self.receive(id); + //setTimeout(function(){handleError();}); + return true; + } + else { + window.onerror = oldWinOnerror; + + if (oldWinOnerror) + return oldWinOnerror.apply(window, arguments); + } + } + http.send(data); + window.onerror = oldWinOnerror; + } + else { + try { + if (binary && http.sendAsBinary) { + binary.blob = getBinaryBlob(data, http, binary); + http.sendAsBinary(binary.blob.data); + } + else + http.send(data); + } + catch(e){ + hasError = true; + } + } + + if (hasError) { + handleError(); + return; + } + else if (binary && http.upload) { + http.upload.onprogress = function(e) { + apf.dispatchEvent("http.uploadprogress", { + loaded : e.loaded - binary.blob.size, + extra : e, + bubbles : true + }); + }; + } + } + + if (!async) { + send.call(this); + return this.receive(id); + } + else { + if (apf.loadsLocalFilesSync && location.protocol == "file:" + && url.indexOf("http://") == -1) { + $setTimeout(function(){ + send.call(_self, true); + }); + } + else + send.call(_self); + + return id; + } + }; + + + /** + * Method that all async objects should implement + * @private + */ + if (!this.exec) { + this.exec = function(method, args, callback, options){ + if (!options) + options = {}; + + var url = args[0], query = ""; + if (!options.method) + options.method = method.toUpperCase(); + if (!options.callback) + options.callback = callback; + + this.contentType = "application/x-www-form-urlencoded"; + this.$get( + apf.getAbsolutePath(apf.config.baseurl, url), + options.method == "GET" + ? options + : apf.extend({data : query}, options) + ); + } + } + + + /** + * Sends the binary blob to server and multipart encodes it if needed this code + * will only be executed on Gecko since it's currently the only browser that + * supports direct file access + * @private + */ + function getBinaryBlob(data, http, binary) { + var boundary = "----apfbound".appendRandomNumber(5), + dashdash = "--", + crlf = "\r\n", + multipartBlob = "", + multipartSize = 0; + + // Build multipart request + if (binary.multipart) { + http.setRequestHeader("Content-Type", "multipart/form-data; boundary=" + boundary); + // Build RFC2388 blob + multipartBlob += dashdash + boundary + crlf + + 'Content-Disposition: form-data; name="' + (binary.filedataname || binary.filename) + + '"; filename="' + binary.filename + '"' + crlf + + 'Content-Type: application/octet-stream' + crlf + crlf + + data + crlf + + dashdash + boundary + dashdash + crlf; + + multipartSize = multipartBlob.length - data.length; + data = multipartBlob; + } + // Send blob or multipart blob depending on config + return {size: multipartSize, data: data}; + } + + /** + * @private + */ + this.receive = function(id){ + if (!this.queue[id]) + return false; + + var qItem = this.queue[id], + http = qItem.http, + callback = qItem.callback; + //if (apf.isGecko) + // var apf = self.apf || apf; // needed here to fix a rare ReferenceError in FF + + + + /*if (self.navigator && navigator.onLine === false && + (location.protocol != "file:" + || qItem.url.indexOf("http://") > -1)) + return false;*/ + + // Test if HTTP object is ready + if (qItem.async) { + try { + if (http.status) {} + } + catch (e) { + var _self = this; + return $setTimeout(function(){ + _self.receive(id) + }, 10); + } + } + + + + //Gonna check for validity of the http response + var errorMessage = [], + extra = { + + tpModule : this, + http : http, + status : http.status, + url : qItem.url, + callback : callback, + id : id, + retries : qItem.retries || 0, + userdata : qItem.options.userdata + }; + + // Check HTTP Status + // The message didn't receive the server. We consider this a timeout (i.e. 12027) + if (http.status > 600) + return this.$timeout(id); + + extra.data = qItem.options.useJSON + ? eval("(" + http.responseText + ")") + : http.responseText; //Can this error? + + if (http.status >= 400 && http.status < 600 || http.status < 10 + && (http.status != 0 || !apf.isIE && !http.responseText)) { //qItem.url.substr(0, 6) == "file:/" + + //@todo This should probably have an RPC specific handler + if (http.status == 401) { + var auth = apf.document.getElementsByTagNameNS(apf.ns.apf, "auth")[0]; + if (auth) { + var wasDelayed = qItem.isAuthDelayed; + qItem.isAuthDelayed = true; + if (auth.authRequired(extra, wasDelayed) === true) + return; + } + } + + + errorMessage.push("HTTP error [" + id + "]:" + http.status + "\n" + + http.responseText); + } + + // Check for XML Errors + if (qItem.options.useXML || this.useXML) { + /* Note (Mike, Oct 14th 2008): for WebDAV, I had to copy the lines below, + it required custom responseXML handling/ + parsing. + If you alter this code, please correct + webdav.js appropriately. + */ + if ((http.responseText || "").replace(/^[\s\n\r]+|[\s\n\r]+$/g, "") == "") + errorMessage.push("Received an empty XML document (0 bytes)"); + else { + try { + var xmlDoc = (http.responseXML && http.responseXML.documentElement) + ? apf.xmlParseError(http.responseXML) + : apf.getXmlDom(http.responseText); + + if (!apf.supportNamespaces) + xmlDoc.setProperty("SelectionLanguage", "XPath"); + + extra.data = xmlDoc.documentElement; + } + catch(e){ + errorMessage.push("Received invalid XML\n\n" + e.message); + } + } + } + + //Process errors if there are any + if (errorMessage.length) { + extra.message = errorMessage.join("\n"); + + + + // Send callback error state + if (!callback || !callback(extra.data, apf.ERROR, extra)) + this.clearQueueItem(id); + + return; + } + + + + + + //Http call was successfull Success + if (!callback || !callback(extra.data, apf.SUCCESS, extra)) + this.clearQueueItem(id); + + return extra.data; + }; + + this.$timeout = function(id){ + if (!this.queue[id]) + return false; + + var qItem = this.queue[id], + http = qItem.http; + + + + // Test if HTTP object is ready + try { + if (http.status) {} + } + catch (e) { + var _self = this; + return $setTimeout(function(){ + _self.$timeout(id) + }, 10); + } + + var callback = qItem.callback; + + http.abort(); + + + + var extra; + var noClear = callback ? callback(null, apf.TIMEOUT, extra = { + + userdata: qItem.options.userdata, + http : http, + url : qItem.url, + tpModule: this, + id : id, + message : "HTTP Call timed out", + retries : qItem.retries || 0 + }) : false; + + + + if (!noClear) + this.clearQueueItem(id); + }; + + /** + * Checks if the request has times out. If so it's retried + * three times before an exception is thrown. Request retrying is a very + * good way to create robust Ajax applications. In many cases, even with + * good connections requests time out. + * @param {Object} extra the information object given as a third + * argument of the http request callback. + * @param {Number} state the return code of the http request. + * Possible values: + * apf.SUCCESS the request was successfull + * apf.TIMEOUT the request has timed out. + * apf.ERROR an error has occurred while making the request. + * apf.OFFLINE the request was made while the application was offline. + * @param {AmlNode} [amlNode] the element receiving the error event. + * @param {Error} [oError] the error to be thrown when the request is + * not retried. + * @param {Number} [maxRetries] the number of retries that are done before + * the request times out. Default is 3. + */ + this.retryTimeout = function(extra, state, amlNode, oError, maxRetries){ + if (state == apf.TIMEOUT + && extra.retries < (maxRetries || apf.maxHttpRetries)) + return extra.tpModule.retry(extra.id); + + oError = oError || new Error(apf.formatErrorString(0, + this, "Communication " + (state == apf.TIMEOUT + ? "timeout" + : "error"), "Url: " + extra.url + "\nInfo: " + extra.message)); + + if ((amlNode || apf).dispatchEvent("error", apf.extend({ + error : oError, + state : state, + extra : extra, + bubbles : true + }, extra)) === false) + return 2; + }; + + /** + * Removes the item from the queue. This is usually done automatically. + * However when the callback returns true the queue isn't cleared, for instance + * when a request is retried. The id of the call + * is found on the 'extra' object. The third argument of the callback. + * Example: + * + * http.clearQueueItem(extra.id); + * + * @param {Number} id the id of the call that should be removed from the queue. + */ + this.clearQueueItem = function(id){ + if (!this.queue[id]) + return false; + + + + if (apf.releaseHTTP && !apf.isGecko) + apf.releaseHTTP(this.queue[id].http); + + this.queue[id] = null; + delete this.queue[id]; + + return true; + }; + + /** + * Retries a call based on it's id. The id of the call is found on the + * 'extra' object. The third argument of the callback. + * Example: + * + * function callback(data, state, extra){ + * if (state == apf.TIMEOUT && extra.retries < apf.maxHttpRetries) + * return extra.tpModule.retry(extra.id); + * + * //Do stuff here + * } + * + * @param {Number} id the id of the call that should be retried. + */ + this.retry = function(id){ + if (!this.queue[id]) + return false; + + var qItem = this.queue[id]; + + + + + + qItem.retries++; + qItem.options.id = id; + this.get(qItem.url, qItem.options); + + return true; + }; + + /** + * see {@link teleport.http.method.clearqueueitem} + */ + this.cancel = function(id){ + if (id === null) + id = this.queue.length - 1; + + if (!this.queue[id]) + return false; + + if (apf.isGecko) + this.queue[id].http.abort(); + + this.clearQueueItem(id); + }; + + if (!this.$loadAml && !this.instantiate && !this.call) { + /** + * @private + */ + this.$loadAml = function(x){ + var receive = this["receive"]; + + for (var i = 0, l = this.childNodes.length; i < l; i++) { + if (this.childNodes[i].nodeType != 1) + continue; + + var url = this.childNodes[i].getAttribute("url"), + callback = self[this.childNodes[i].getAttribute("receive") || receive], + options = { + useXML : this.childNodes[i].getAttribute("type") == "XML", + async : !apf.isFalse(this.childNodes[i].getAttribute("async")) + }; + + this[this.childNodes[i].getAttribute("name")] = function(data, userdata){ + options.userdata = userdata; + options.data = data; + return this.get(url, options); + } + } + }; + + /** + * @private + */ + this.instantiate = function(x){ + var url = x.getAttribute("src"), + options = { + async : x.getAttribute("async") != "false", + nocache : true + }; + + this.getURL = function(data, userdata){ + options.data = data; + options.userdata = userdata; + options.callback = this.callbacks.getURL; + return this.get(url, options); + } + + var name = "http" + Math.round(Math.random() * 100000); + apf.setReference(name, this); + + return name + ".getURL()"; + }; + + /** + * @private + */ + this.call = function(method, args){ + this[method].call(this, args); + }; + } +}; + + + +apf.Init.run("http"); + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/core/lib/teleport/iframe.js)SIZE(5720)TIME(Fri, 14 Oct 2011 08:01:44 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/core/lib/teleport/socket.js)SIZE(19222)TIME(Fri, 14 Oct 2011 08:01:44 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: socket://www.fsf.org. + * + */ + + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/core/lib/uirecorder/capture.js)SIZE(21922)TIME(Wed, 30 Nov 2011 17:29:20 GMT)*/ + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/core/lib/uirecorder/playback.js)SIZE(28844)TIME(Wed, 30 Nov 2011 17:29:20 GMT)*/ + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/core/lib/uirecorder/selenium.js)SIZE(9161)TIME(Tue, 06 Dec 2011 16:22:54 GMT)*/ + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/core/lib/uirecorder/ui.js)SIZE(18464)TIME(Wed, 30 Nov 2011 17:29:20 GMT)*/ + + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/core/markup/domparser.js)SIZE(16889)TIME(Tue, 13 Dec 2011 13:33:58 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + +/** + * The parser of the Ajax.org Markup Language. Besides aml this parser takes care + * of distributing parsing tasks to other parsers like the native html parser and + * the xsd parser. + * @parser + * @private + * + * @define include element that loads another aml files. + * Example: + * + * + * + * @attribute {String} src the location of the aml file to include in this application. + * @addnode global, anyaml + */ +apf.DOMParser = function(){}; + +/* + @todo the shouldWait variable should be tree based and checked recursively up +*/ +apf.DOMParser.prototype = new (function(){ + this.caseInsensitive = true; + this.preserveWhiteSpace = false; //@todo apf3.0 whitespace issue + + /* + @todo domParser needs to get a queue based on the parentNode that is + waiting to be parsed. This will prevent collisions when multiple + parts of the document are altered at the same time. + */ + this.$shouldWait = 0; + + // privates + var RE = [ + /\<\!(DOCTYPE|doctype)[^>]*>/, + / /g, + /<\s*\/?\s*(?:\w+:\s*)[\w-]*[\s>\/]/g + ], + XPATH = "//@*[not(contains(local-name(), '.')) and not(translate(local-name(), 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz') = local-name())]"; + + this.parseFromString = function(xmlStr, mimeType, options){ + var xmlNode; + if (this.caseInsensitive) { + //replace(/&\w+;/, ""). replace this by something else + //.replace(RE[1], " ") + var str = xmlStr.replace(RE[0], "") + .replace(RE[2], //.replace(/^[\r\n\s]*/, "") + function(m){ return m.toLowerCase(); }); + + /* @todo apf3.0 integrate this + x.ownerDocument.setProperty("SelectionNamespaces", + "xmlns:a='" + apf.ns.aml + "'"); + */ + + if (!this.supportNamespaces) + str = str.replace(/xmlns\=\"[^"]*\"/g, ""); + + + xmlNode = apf.getXmlDom(str, null, this.preserveWhiteSpace || apf.debug).documentElement; + var i, l, + nodes = xmlNode.selectNodes(XPATH); + // Case insensitive support + for (i = 0, l = nodes.length; i < l; i++) { + (nodes[i].ownerElement || nodes[i].selectSingleNode("..")) + .setAttribute(nodes[i].nodeName.toLowerCase(), nodes[i].nodeValue); + } + + } + else { + xmlNode = apf.getXmlDom(xmlStr, null, this.preserveWhiteSpace || apf.debug).documentElement; + } + + return this.parseFromXml(xmlNode, options); + }; + + //@todo prevent leakage by not recording .$aml + this.parseFromXml = function(xmlNode, options){ + var doc, docFrag, amlNode, beforeNode; + if (!options) + options = {}; + + if (!options.delayedRender && !options.include) { + //Create a new document + if (options.doc) { + doc = options.doc; + docFrag = options.docFrag || doc.createDocumentFragment(); + } + else { + doc = new apf.AmlDocument(); + doc.$aml = xmlNode; + doc.$domParser = this; + } + if (options.host) + doc.$parentNode = options.host; //This is for sub docs that need to access the outside tree + + + + //Let's start building our tree + amlNode = this.$createNode(doc, xmlNode.nodeType, xmlNode); //Root node + (docFrag || doc).appendChild(amlNode); + if (options.htmlNode) + amlNode.$int = options.htmlNode; + } + else { + amlNode = options.amlNode; + doc = options.doc; + + if (options.include) { + var n = amlNode.childNodes; + var p = n.indexOf(options.beforeNode); + var rest = p ? n.splice(p, n.length - p) : []; + } + } + + //Set parse context + this.$parseContext = [amlNode, options]; + + //First pass - Node creation + var nodes, nodelist = {}, prios = [], _self = this; + var recur; + (recur = function(amlNode, nodes){ + var cL, newNode, node, nNodes, + cNodes = amlNode.childNodes, + i = 0, + l = nodes.length; + for (; i < l; i++) { + //Create child + newNode = _self.$createNode(doc, (node = nodes[i]).nodeType, node); + if (!newNode) continue; //for preserveWhiteSpace support + + cNodes[cL = cNodes.length] = newNode; //Add to children + + //Set tree refs + newNode.parentNode = amlNode; + if (cL > 0) + (newNode.previousSibling = cNodes[cL - 1]).nextSibling = newNode; + + //Create children + if (!newNode.render && newNode.canHaveChildren && (nNodes = node.childNodes).length) + recur(newNode, nNodes); + + //newNode.$aml = node; //@todo should be deprecated... + + //Store high prio nodes for prio insertion + if (newNode.$parsePrio) { + if (newNode.$parsePrio == "001") { + newNode.dispatchEvent("DOMNodeInsertedIntoDocument"); //{relatedParent : nodes[j].parentNode} + continue; + } + + (nodelist[newNode.$parsePrio] || (prios.push(newNode.$parsePrio) + && (nodelist[newNode.$parsePrio] = []))).push(newNode); //for second pass + } + } + + amlNode.firstChild = cNodes[0]; + amlNode.lastChild = cNodes[cL]; + })(amlNode, xmlNode.childNodes); + + if (options.include && rest.length) { + var index = n.length - 1; + n.push.apply(n, rest); + var last = n[index]; + var next = n[index + 1]; + (next.previousSibling = last).nextSibling = next; + amlNode.lastChild = n[n.length - 1]; + } + + if (options.delay) { + amlNode.$parseOptions = { + prios: prios, + nodelist: nodelist + }; + return (docFrag || doc); + } + + //Second pass - Document Insert signalling + prios.sort(); + var i, j, l, l2; + for (i = 0, l = prios.length; i < l; i++) { + nodes = nodelist[prios[i]]; + for (j = 0, l2 = nodes.length; j < l2; j++) { + nodes[j].dispatchEvent("DOMNodeInsertedIntoDocument"); //{relatedParent : nodes[j].parentNode} + } + } + + if (this.$shouldWait) + return (docFrag || doc); + + if (options.timeout) { + $setTimeout(function(){ + _self.$continueParsing(amlNode, options); + }); + } + else { + this.$continueParsing(amlNode, options); + } + + return (docFrag || doc); + }; + + this.$callCount = 0; + this.$continueParsing = function(amlNode, options){ + if (this.$shouldWait && --this.$shouldWait != 0) + return false; + + if (!options) + options = {}; + + this.$callCount++; + + if (amlNode.$parseOptions) { + var prios = amlNode.$parseOptions.prios, + nodelist = amlNode.$parseOptions.nodelist, + i, j, l, l2, node; + delete amlNode.$parseOptions; + + //Second pass - Document Insert signalling + prios.sort(); + for (i = 0, l = prios.length; i < l; i++) { + var nodes = nodelist[prios[i]]; + for (j = 0, l2 = nodes.length; j < l2; j++) { + if (!(node = nodes[j]).parentNode || node.$amlLoaded) //@todo generalize this using compareDocumentPosition + continue; + nodes[j].dispatchEvent("DOMNodeInsertedIntoDocument"); //{relatedParent : nodes[j].parentNode} + } + } + } + + //instead of $amlLoaded use something more generic see compareDocumentPosition + if (!options.ignoreSelf && !amlNode.$amlLoaded) + amlNode.dispatchEvent("DOMNodeInsertedIntoDocument"); //{relatedParent : nodes[j].parentNode} + + //Recursively signal non prio nodes + (function _recur(nodes){ + var node, nNodes; + for (var i = 0, l = nodes.length; i < l; i++) { + if (!(node = nodes[i]).$parsePrio && !node.$amlLoaded) { + node.dispatchEvent("DOMNodeInsertedIntoDocument"); //{relatedParent : nodes[j].parentNode} + } + + //Create children + if (!node.render && (nNodes = node.childNodes).length) + _recur(nNodes); + } + })(amlNode.childNodes); + + if (!--this.$callCount && !options.delay) + apf.queue.empty(); + + if (options.callback) + options.callback.call(amlNode.ownerDocument); + + delete this.$parseContext; + }; + + this.$createNode = function(doc, nodeType, xmlNode, namespaceURI, nodeName, nodeValue){ + var o; + + switch (nodeType) { + case 1: + var id, prefix; + if (xmlNode) { + if ((namespaceURI = xmlNode.namespaceURI || apf.ns.xhtml) + && !(prefix = doc.$prefixes[namespaceURI])) { + doc.$prefixes[prefix = xmlNode.prefix || xmlNode.scopeName || ""] = namespaceURI; + doc.$namespaceURIs[namespaceURI] = prefix; + + if (!doc.namespaceURI && !prefix) { + doc.namespaceURI = namespaceURI; + doc.prefix = prefix; + } + } + nodeName = xmlNode.baseName || xmlNode.localName || xmlNode.tagName.split(":").pop(); + } + else { + prefix = doc.$prefixes[namespaceURI] || ""; + } + + + + var els = apf.namespaces[namespaceURI].elements; + + + + o = new (els[nodeName] || els["@default"])(null, nodeName); + + o.prefix = prefix || ""; + o.namespaceURI = namespaceURI; + o.tagName = prefix ? prefix + ":" + nodeName : nodeName; + + if (xmlNode) { + if ((id = xmlNode.getAttribute("id")) && !self[id]) + o.$propHandlers["id"].call(o, o.id = id); + + //attributes + var attr = xmlNode.attributes, n; + for (var a, i = 0, l = attr.length; i < l; i++) { + o.attributes.push(new apf.AmlAttr(o, + (n = (a = attr[i]).nodeName), a.nodeValue)); + + if (n == "render") + o.render = true; + + } + } + + break; + case 2: + o = new apf.AmlAttr(); + o.name = o.nodeName = nodeName; + if (nodeValue || (nodeValue = xmlNode && xmlNode.nodeValue)) + o.value = o.nodeValue = nodeValue; + + if (xmlNode) { + if (xmlNode.namespaceURI && !(o.prefix = doc.$namespaceURIs[o.namespaceURI = xmlNode.namespaceURI])) + doc.$prefixes[o.prefix = xmlNode.prefix || xmlNode.scopeName] = o.namespaceURI; + } + else { + o.prefix = doc.$prefixes[namespaceURI]; + } + + break; + case 3: + if (xmlNode) + nodeValue = xmlNode && xmlNode.nodeValue; + if (!this.preserveWhiteSpace && !(nodeValue || "").trim()) + return; + + o = new apf.AmlText(); + o.nodeValue = nodeValue || xmlNode && xmlNode.nodeValue; + break; + case 7: + var target = nodeName || xmlNode && xmlNode.nodeName; + + o = new apf.aml.processingInstructions[target](); + + o.target = o.nodeName = target; + o.data = o.nodeValue = nodeValue || xmlNode && xmlNode.nodeValue; + break; + case 4: + o = new apf.AmlCDATASection(); + o.nodeValue = nodeValue || xmlNode && xmlNode.nodeValue; + break; + case 5: //unsupported + o = new apf.AmlNode(); + o.nodeType = nodeType; + break; + case 6: //unsupported + o = new apf.AmlNode(); + o.nodeType = nodeType; + break; + case 8: + o = new apf.AmlComment(); + o.nodeValue = nodeValue || xmlNode && xmlNode.nodeValue; + break; + case 9: + o = new apf.AmlDocument(); + o.$domParser = this; + break; + case 10: //unsupported + o = new apf.AmlNode(); + o.nodeType = nodeType; + break; + case 11: + o = new apf.AmlDocumentFragment(); + break; + } + + o.ownerDocument = doc; + o.$aml = xmlNode; + + return o; + }; +})(); + +/** + * + * @author Ruben Daniels (ruben AT ajax DOT org) + * @version %I%, %G% + * @since 0.8 + */ +apf.AmlNamespace = function(){ + this.elements = {}; + this.processingInstructions = {}; +}; + +apf.AmlNamespace.prototype = { + setElement : function(tagName, fConstr){ + return this.elements[tagName] = fConstr; + }, + + setProcessingInstruction : function(target, fConstr){ + this.processingInstructions[target] = fConstr; + } +}; + + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/core/markup/aml.js)SIZE(1478)TIME(Fri, 14 Oct 2011 08:01:44 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + +/** + * The parser of the Ajax.org Markup Language. Besides aml this parser takes care + * of distributing parsing tasks to other parsers like the native html parser and + * the xsd parser. + * @parser + * @private + * + * @define include element that loads another aml files. + * Example: + * + * + * + * @attribute {String} src the location of the aml file to include in this application. + * @addnode global, anyaml + */ +apf.aml = new apf.AmlNamespace(); +apf.setNamespace("http://ajax.org/2005/aml", apf.aml); + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/core/markup/aml/node.js)SIZE(22555)TIME(Tue, 06 Dec 2011 16:22:54 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + +apf.__AMLNODE__ = 1 << 14; + + + +/** + * All elements inheriting from this {@link term.baseclass baseclass} have Document Object Model (DOM) support. The DOM + * is the primary method for accessing and manipulating an xml document. This + * includes html documents and aml documents. Every element in the ajax.org + * markup language can be manipulated using the W3C DOM. This means + * that every element and attribute you can set in the xml format, can be + * changed, set, removed reparented, etc runtime. This offers a great deal of + * flexibility. Well known methods + * from this specification are .appendChild .removeChild .setAttribute and + * insertBefore to name a few. Ajax.org Platform aims to implement DOM1 + * completely and parts of DOM2. Which should be extended in the future to fully + * implement DOM Level 2. For more information see {@link http://www.w3.org/DOM/} + * or {@link http://www.w3schools.com/dom/default.asp}. + * Example: + * Ajax.org Markup Language + * + * + * + * + * + * Document Object Model in javascript + * + * //The following line is only there for completeness sake. In fact apf + * //automatically adds a reference in javascript called winExample based + * //on the id it has. + * var winExample = apf.document.getElementById("winExample"); + * winExample.setAttribute("title", "Example"); + * winExample.setAttribute("icon", "icoFolder.gif"); + * winExample.setAttribute("left", "100"); + * + * var lblNew = apf.document.createElement("label"); + * winExample.appendChild(lblNew); + * lblNew.setAttribute("caption", "Example"); + * + * tstButton.setAttribute("caption", "Click me"); + * + * That would be the same as having the following aml: + * + * + * + * + * + * + * Remarks: + * Because the W3C DOM is native to all modern browsers the internet is full + * of tutorials and documentation for this API. If you need more information + * it's a good idea to search for tutorials online. + * + * @event DOMNodeInserted + * @event DOMNodeInsertedIntoDocument + * @event DOMNodeRemoved + * @event DOMNodeRemovedFromDocument + * + * @constructor + * @baseclass + * + * @author Ruben Daniels (ruben AT ajax DOT org) + * @version %I%, %G% + * @since 0.5 + */ +apf.AmlNode = function(){ + this.$init(function(){ + /** + * Nodelist containing all the child nodes of this element. + */ + this.childNodes = []; //@todo AmlNodeList + }); +}; + +(function() { + + + /** + * Returns a string representation of this object. + */ + this.toString = function(){ + if (this.nodeName) + return "[" + this.nodeName.uCaseFirst() + " Node]"; + + return "[" + this.localName.uCaseFirst() + " Element Node, <" + + (this.prefix ? this.prefix + ":" : "") + this.localName + " " + + this.attributes.join(" ") + + " /> : " + (this.name || this.$uniqueId || "") + "]"; + }; + + + /** + * Number specifying the type of node within the document. + */ + this.$regbase = this.$regbase | apf.__AMLNODE__; + + /** + * Constant for a dom element node. + * @type {Number} + */ + this.NODE_ELEMENT = 1; + /** + * Constant for a dom attribute node. + * @type {Number} + */ + this.NODE_ATTRIBUTE = 2; + /** + * Constant for a dom text node. + * @type {Number} + */ + this.NODE_TEXT = 3; + /** + * Constant for a dom cdata section node. + * @type {Number} + */ + this.NODE_CDATA_SECTION = 4; + /** + * Constant for a dom entity reference node. + * @type {Number} + */ + this.NODE_ENTITY_REFERENCE = 5; + /** + * Constant for a dom entity node. + * @type {Number} + */ + this.NODE_ENTITY = 6; + /** + * Constant for a dom processing instruction node. + * @type {Number} + */ + this.NODE_PROCESSING_INSTRUCTION = 7; + /** + * Constant for a dom comment node. + * @type {Number} + */ + this.NODE_COMMENT = 8; + /** + * Constant for a dom document node. + * @type {Number} + */ + this.NODE_DOCUMENT = 9; + /** + * Constant for a dom document type node. + * @type {Number} + */ + this.NODE_DOCUMENT_TYPE = 10; + /** + * Constant for a dom document fragment node. + * @type {Number} + */ + this.NODE_DOCUMENT_FRAGMENT = 11; + /** + * Constant for a dom notation node. + * @type {Number} + */ + this.NODE_NOTATION = 12; + + + + /** + * The document node of this application + */ + this.ownerDocument = null; + + /** + * Returns the value of the current node. + */ + this.nodeValue = ""; + + /** + * The namespace URI of the node, or null if it is unspecified (read-only). + * When the node is a document, it returns the XML namespace for the current + * document. + */ + this.namespaceURI = ""; + + /** + * @todo + */ + //this.baseURI = alsdjlasdj + + /** + * @todo + */ + //this.prefix = asdkljahqsdkh + + /** + * Appends an element to the end of the list of children of this element. + * If the element was already a child of another element it is removed from + * that parent before adding it this element. + * + * @param {AmlNode} amlNode the element to insert as child of this element. + * @return {AmlNode} the appended node + * @method + */ + this.appendChild = + + /** + * Inserts an element before another element in the list of children of this + * element. * If the element was already a child of another element it is + * removed from that parent before adding it this element. + * + * @param {AmlNode} amlNode the element to insert as child of this element. + * @param {AmlNode} beforeNode the element which determines the insertion position of the element. + * @return {AmlNode} the inserted node + */ + this.insertBefore = function(amlNode, beforeNode, noHtmlDomEdit){ + + + if (this.nodeType == this.NODE_DOCUMENT) { + if (this.childNodes.length) { + throw new Error(apf.formatErrorString(0, this, + "Insertbefore DOM operation", + "Only one top level element is allowed in an AML document.")); + } + else this.documentElement = amlNode; //@todo apf3.0 removal + } + + if (amlNode == beforeNode) + return amlNode; + + if (this == amlNode) { + throw new Error(apf.formatErrorString(0, this, + "Insertbefore DOM operation", + "Cannot append node as a child of itself.")); + } + + if (amlNode.nodeType == this.NODE_DOCUMENT_FRAGMENT) { + var nodes = amlNode.childNodes.slice(0); + for (var i = 0, l = nodes.length; i < l; i++) { + this.insertBefore(nodes[i], beforeNode); + } + return amlNode; + } + + var isMoveWithinParent = amlNode.parentNode == this, + oldParentHtmlNode = amlNode.$pHtmlNode, + oldParent = amlNode.parentNode, + index = -1, + _self = this; + + if (beforeNode) { + index = this.childNodes.indexOf(beforeNode); + if (index < 0) { + + + return false; + } + } + + if (!amlNode.ownerDocument) + amlNode.ownerDocument = this.ownerDocument || apf.ownerDocument; + + if (amlNode.parentNode) + amlNode.removeNode(isMoveWithinParent, true);//noHtmlDomEdit); + amlNode.parentNode = this; + + if (beforeNode) + index = this.childNodes.indexOf(beforeNode); + + if (beforeNode) { + amlNode.nextSibling = beforeNode; + amlNode.previousSibling = beforeNode.previousSibling; + beforeNode.previousSibling = amlNode; + if (amlNode.previousSibling) + amlNode.previousSibling.nextSibling = amlNode; + } + + if (index >= 0) { + this.childNodes = this.childNodes.slice(0, index).concat(amlNode, + this.childNodes.slice(index)); + } + else { + index = this.childNodes.push(amlNode) - 1; + + amlNode.nextSibling = null; + if (index > 0) { + amlNode.previousSibling = this.childNodes[index - 1]; + amlNode.previousSibling.nextSibling = amlNode; + } + else { + amlNode.previousSibling = null; + } + } + + this.firstChild = this.childNodes[0]; + this.lastChild = this.childNodes[this.childNodes.length - 1]; + + //@todo fix event struture, fix tree events + var initialAppend = !amlNode.$amlLoaded; + function triggerUpdate(){ + amlNode.$pHtmlNode = _self.canHaveChildren ? _self.$int : document.body; + + //@todo this is a hack, a good solution should be found + if (document.adoptNode && amlNode.$ext && amlNode.$ext.nodeType == 1) { + var reappendlist = []; + var iframelist = apf.getArrayFromNodelist( + amlNode.$ext.getElementsByTagName("iframe")); + if (amlNode.$ext.tagName == "IFRAME") + document.adoptNode(amlNode.$ext); + + for (var i = 0; i < iframelist.length; i++) { + reappendlist[i] = [ + iframelist[i].parentNode, + iframelist[i].nextSibling, + document.adoptNode(iframelist[i]), + ] + } + } + + var nextNode = beforeNode; + if (!initialAppend && !noHtmlDomEdit && amlNode.$ext && !amlNode.$coreHtml) { + nextNode = beforeNode; + while (nextNode && !(nextNode.$altExt || nextNode.$ext)) { + nextNode = nextNode.nextSibling; + } + + amlNode.$pHtmlNode.insertBefore(amlNode.$altExt || amlNode.$ext, + nextNode && (nextNode.$altExt || nextNode.$ext) || null); + + for (var i = reappendlist.length - 1; i >= 0; i--) { + reappendlist[i][0].insertBefore( + reappendlist[i][2], + reappendlist[i][1]); + } + reappendlist = []; + } + + //Signal node and all it's ancestors + amlNode.dispatchEvent("DOMNodeInserted", { + $beforeNode : beforeNode, + relatedNode : _self, + $isMoveWithinParent : isMoveWithinParent, + $oldParentHtmlNode : oldParentHtmlNode, + $oldParent : oldParent, + bubbles : true + }); + + if (initialAppend && !noHtmlDomEdit && beforeNode && amlNode.$ext && !amlNode.$coreHtml) { + nextNode = beforeNode; + while (nextNode && !(nextNode.$altExt || nextNode.$ext)) { + nextNode = nextNode.nextSibling; + } + + amlNode.$pHtmlNode.insertBefore(amlNode.$altExt || amlNode.$ext, + nextNode && (nextNode.$altExt || nextNode.$ext) || null); + + for (var i = reappendlist.length - 1; i >= 0; i--) { + reappendlist[i][0].insertBefore( + reappendlist[i][2], + reappendlist[i][1]); + } + } + } + + var doc = this.nodeType == this.NODE_DOCUMENT ? this : this.ownerDocument; + if (!doc || doc.$domParser.$shouldWait) + return amlNode; + + if (this.nodeType == this.NODE_DOCUMENT_FRAGMENT) + return; //We don't update the tree if this is a doc fragment + + //@todo review this... + if (initialAppend && !amlNode.render) { // && (nNodes = node.childNodes).length ?? + (this.ownerDocument || this).$domParser.$continueParsing(amlNode, {delay: true}); + } + + triggerUpdate(); + return amlNode; + }; + + /** + * Removes this element from the document hierarchy. Call-chaining is + * supported. + * + */ + this.removeNode = function(doOnlyAdmin, noHtmlDomEdit){ + + + if (!this.parentNode || !this.parentNode.childNodes) + return this; + + + + this.parentNode.childNodes.remove(this); + + //If we're not loaded yet, just remove us from the aml to be parsed + if (this.$amlLoaded && !apf.isDestroying) { + //this.parentNode.$aml.removeChild(this.$aml); + + this.dispatchEvent("DOMNodeRemoved", { + relatedNode : this.parentNode, + bubbles : true, + $doOnlyAdmin : doOnlyAdmin + }); + + if (!noHtmlDomEdit && !doOnlyAdmin && this.$ext && this.$ext.parentNode) { + this.$ext.parentNode.removeChild(this.$ext); + //delete this.$ext; //WTF??? + } + } + + if (this.parentNode.firstChild == this) + this.parentNode.firstChild = this.nextSibling; + if (this.parentNode.lastChild == this) + this.parentNode.lastChild = this.previousSibling; + + if (this.nextSibling) + this.nextSibling.previousSibling = this.previousSibling; + if (this.previousSibling) + this.previousSibling.nextSibling = this.nextSibling; + + this.$pHtmlNode = + this.parentNode = + this.previousSibling = + this.nextSibling = null; + + return this; + }; + + /** + * Removes a child from the node list of this element. Call-chaining is + * supported. + */ + this.removeChild = function(childNode) { + + + childNode.removeNode(); + return this; + }; + + //@todo + this.replaceChild = function(){}; + + /** + * Clones this element, creating an exact copy of it but does not insert + * it in the document hierarchy. + * @param {Boolean} deep whether the element's are cloned recursively. + * @return {AmlNode} the cloned element. + */ + this.cloneNode = function(deep){ + if (deep && this.nodeType == 1) { + return this.ownerDocument.$domParser.parseFromXml(this, { + doc : this.ownerDocument, + delay : true + }).childNodes[0]; + } + else { + return this.ownerDocument.$domParser.$createNode( + this.ownerDocument, this.nodeType, this); + } + }; + + //@todo + this.canDispatch = function(namespaceURI, type){}; + + //@todo + this.compareDocumentPosition = function(otherNode){ + /* + DOCUMENT_POSITION_DISCONNECTED = 0x01; + DOCUMENT_POSITION_PRECEDING = 0x02; + DOCUMENT_POSITION_FOLLOWING = 0x04; + DOCUMENT_POSITION_CONTAINS = 0x08; + DOCUMENT_POSITION_CONTAINED_BY = 0x10; + */ + }; + + this.hasAttributes = function(){ + return this.attributes && this.attributes.length; + }; + + this.hasChildNodes = function(){ + return this.childNodes && this.childNodes.length; + }; + + this.isDefaultNamespace = function(namespaceURI){ + if (node.nodeType == 1) { + if (!this.prefix) + return this.namespaceURI == namespaceURI; + + //@todo Loop through attributes here + } + + var node = this.parentNode || this.ownerElement; + return node && node.isDefaultNamespace(namespaceURI); + }; + + this.lookupNamespaceURI = function(prefix){ + if (node.nodeType == 1) { + if (this.namespaceURI && prefix == this.prefix) + return this.namespaceURI ; + + //@todo Loop through attributes here + } + + var node = this.parentNode || this.ownerElement; + return node && node.lookupNamespaceURI(prefix); + }; + + this.lookupPrefix = function(namespaceURI){ + if (this.nodeType == 1) { + if (namespaceURI == this.namespaceURI && this.prefix) + return this.prefix; + + //@todo Loop through attributes here + } + + var node = this.parentNode || this.ownerElement; + return node && node.lookupPrefix(namespaceURI); + }; + + this.normalize = function(){}; + + /**** Xpath support ****/ + + /** + * Queries the aml dom using the W3C xPath query language and returns a node + * list. This is not an official API call but can be useful in certain cases. + * see {@link core.documentimplementation.method.evaluate evaluate on the apf.document} + * @param {String} sExpr the xpath expression to query the aml DOM tree with. + * @param {AmlNode} [contextNode] the element that serves as the starting point of the search. Defaults to this element. + * @returns {NodeList} list of found nodes. + */ + this.selectNodes = function(sExpr, contextNode){ + if (!apf) return; + + if (!apf.XPath) + apf.runXpath(); + return apf.XPath.selectNodes(sExpr, + contextNode || (this.nodeType == 9 ? this.documentElement : this)); + }; + + /** + * Queries the aml dom using the W3C xPath query language and returns a single + * node. This is not an official API call but can be useful in certain cases. + * see {@link core.documentimplementation.method.evaluate evaluate on the apf.document} + * @param {String} sExpr the xpath expression to query the aml DOM tree with. + * @param {AmlNode} [contextNode] the element that serves as the starting point of the search. Defaults to this element. + * @returns {AmlNode} the first node that matches the query. + */ + this.selectSingleNode = function(sExpr, contextNode){ + if (!apf) return; + + if (!apf.XPath) + apf.runXpath(); + return apf.XPath.selectNodes(sExpr, + contextNode || (this.nodeType == 9 ? this.documentElement : this))[0]; + }; + + /*this.addEventListener("DOMNodeInsertedIntoDocument", function(e){ + + }, true);*/ +}).call(apf.AmlNode.prototype = new apf.Class()); + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/core/markup/aml/element.js)SIZE(21909)TIME(Tue, 13 Dec 2011 13:33:58 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + +apf.AmlElement = function(struct, tagName){ + var $init = this.$init; + this.$init = function(tagName, nodeFunc, struct){ + this.$supportedProperties = this.$supportedProperties.slice(); + + var prop, p, q; + p = this.$propHandlers; + q = this.$propHandlers = {}; + for (prop in p) + q[prop] = p[prop]; + + p = this.$booleanProperties; + q = this.$booleanProperties = {}; + for (prop in p) + q[prop] = p[prop]; + + return $init.call(this, tagName, nodeFunc, struct); + }; + + this.$init(function(tagName, nodeFunc, struct){ + this.$events = {}; + this.$inheritProperties = {}; + + /** + * Nodelist containing all attributes. This is implemented according to the + * W3C specification. + * Example: + * + * for (var i = 0; i < obj.attributes.length; i++) { + * alert(obj.attributes.item(i)); + * } + * + * @see baseclass.amldom.method.getAttribute + * @see baseclass.amldom.method.setAttribute + */ + this.attributes = new apf.AmlNamedNodeMap(this); //@todo apf3.0 move to init? + + /** + * The purpose of this element + * Possible values: + * apf.NODE_VISIBLE this element has a gui representation + * apf.NODE_HIDDEN this element does not display a gui + */ + this.nodeFunc = nodeFunc; + + /** + * The local name of this element + */ + this.localName = tagName; //@todo + + //Parse struct to create attributes and child nodes + if (struct) { + var nodes, prop, i, l; + if (struct.childNodes) { + nodes = struct.childNodes; + delete struct.childNodes; //why delete? + } + + //Attributes + for (prop in struct){ + if (prop == "htmlNode") continue; + + this.attributes.push(new apf.AmlAttr(this, prop, struct[prop])); + } + + if (!this.ownerDocument) { + this.ownerDocument = apf.document; + this.prefix = "a"; + this.namespaceURI = apf.ns.aml; + this.tagName = tagName; + } + + if (nodes) { + this.childNodes = nodes; + + for (i = 0, l = nodes.length; i < l; i++) { + nodes[i].nextSibling = nodes[i + 1] || null; + nodes[i].previousSibling = nodes[i - 1] || null; + nodes[i].parentNode = this; + } + this.firstChild = nodes[0] || null; + this.lastChild = nodes[nodes.length - 1] || null; + } + + //Temp hack + this.$aml = apf.$emptyNode || (apf.$emptyNode = apf.getXml("")); + } + }); + + if (tagName) //of typeof is not function and not true + $init.call(this, tagName, apf.NODE_HIDDEN, struct); +}; + +(function(){ + /** + * Number specifying the type of node within the document. + */ + this.nodeType = this.NODE_ELEMENT; + this.canHaveChildren = true; + + this.$propHandlers = { + /** + * @attribute {String} id the identifier of this element. When set this + * identifier is the name of the variable in javascript to access this + * element directly. This identifier is also the way to get a reference to + * this element using apf.document.getElementById. + * Example: + * + * + * + * alert(barExample); + * + * + */ + "id": function(value){ + + + if (this.name == value) + return; + + if (self[this.name] == this) + self[this.name] = null + + if (!self[value] || !self[value].hasFeature) { + try { + self[value] = this; + } + catch(ex) { + + } + } + + + //@todo dispatch event for new name creation. + //@todo old name disposal + + apf.nameserver.register(this.localName, value, this) + + + this.name = value; + } + }; + + this.$booleanProperties = {}; + this.$inheritProperties = {}; + this.$supportedProperties = []; + + /** + * Returns a list of elements with the given tag name. + * The subtree below the specified element is searched, excluding the + * element itself. + * + * @method + * @param {String} tagName the tag name to look for. The special string "*" represents any tag name. + * @return {NodeList} containing any node matching the search string + */ + this.getElementsByTagName = function(tagName, norecur){ + tagName = tagName.toLowerCase(); + var node, i, l, + nodes = this.childNodes, + result = []; + for (i = 0, l = nodes.length; i < l; i++) { + if ((node = nodes[i]).nodeType != 1) + continue; + + if (node.tagName == tagName || tagName == "*") + result.push(node); + + if (!norecur && node.nodeType == 1) + result = result.concat(node.getElementsByTagName(tagName)); + } + + return result; + }; + + this.getElementsByTagNameNS = function(namespaceURI, localName, norecur){ + localName = localName.toLowerCase(); + var node, i, l, + nodes = this.childNodes, + result = []; + for (i = 0, l = nodes.length; i < l; i++) { + if ((node = nodes[i]).nodeType != 1) + continue; + + if (node.namespaceURI == namespaceURI && (node.localName == localName || localName == "*")) + result.push(node); + + if (!norecur && node.nodeType == 1) + result = result.concat(node.getElementsByTagNameNS(namespaceURI, localName)); + } + + return result; + }; + + /** + * Sets an attribute on this element. Call-chaining is supported. + * @param {String} name the name of the attribute to which the value is set + * @param {String} value the new value of the attribute. + */ + this.setAttribute = function(name, value, noTrigger) { + name = name.toLowerCase(); + + var a = this.attributes.getNamedItem(name); + if (!a) { + this.attributes.push(a = new apf.AmlAttr(this, name, value)); + + if (!this.$amlLoaded) + return; + + if (noTrigger) + a.$setValue(value); + else { + //@todo apf3.0 domattr + a.dispatchEvent("DOMNodeInsertedIntoDocument", { + relatedNode : this + }); + + //@todo apf3.0 domattr + a.dispatchEvent("DOMNodeInserted", { + relatedNode : this, + bubbles : true + }); + } + + return; + } + + var oldValue = a.nodeValue; + a.$setValue(value); + + if (noTrigger || !this.$amlLoaded) + return; + + //@todo apf3.0 domattr + a.$triggerUpdate(null, oldValue); + }; + + //@todo apf3.0 domattr + this.setAttributeNode = function(attrNode){ + this.attributes.setNamedItem(attrNode); + }; + + this.setAttributeNS = function(namespaceURI, name, value){ + return this.setAttribute(name, value); + }; + + //@todo apf3.0 domattr + this.hasAttribute = function(name){ + return this.getAttributeNode(name) ? true : false; + }; + + //@todo + this.hasAttributeNS = function(namespaceURI, name){ + return this.hasAttribute(name); + }; + + /** + * Removes an attribute from this element. Call-chaining is supported. + * @param {String} name the name of the attribute to remove. + */ + //@todo apf3.0 domattr + this.removeAttribute = function(name){ + this.attributes.removeNamedItem(name); + return this; + }; + + //@todo apf3.0 domattr + this.removeAttributeNS = function(namespaceURI, name){ + return this.removeAttribute(name); + }; + + //@todo apf3.0 domattr + this.removeAttributeNode = function(attrNode){ + this.attributes.removeNamedItem(attrNode.name); //@todo this should probably be slightly different. + }; + + /** + * Retrieves the value of an attribute of this element + * @param {String} name the name of the attribute for which to return the value. + * @param {Boolean} [inherited] + * @return {String} the value of the attribute or null if none was found with the name specified. + * @method + */ + this.getAttribute = function(name, inherited){ + var item = this.attributes.getNamedItem(name); + return item ? (inherited + ? item.inheritedValue || item.nodeValue + : item.nodeValue) : null; + }; + + /** + * Retrieves the attribute node for a given name + * @param {String} name the name of the attribute to find. + * @return {AmlNode} the attribute node or null if none was found with the name specified. + */ + this.getAttributeNode = function(name){ + return this.attributes.getNamedItem(name); + }; + + this.getBoundingClientRect = function(){ + return new apf.AmlTextRectangle(this); + }; + + //@todo + this.querySelector = function(){ + // here we should use: http://code.google.com/p/css2xpath/source/browse/trunk/src/css2xpath.js + }; + + //@todo + this.querySelectorAll = function(){ + // here we should use: http://code.google.com/p/css2xpath/source/browse/trunk/src/css2xpath.js + }; + + //@todo + this.scrollIntoView = function(){ + + }; + + /** + * Replaces the child aml elements with new aml. + * @param {mixed} amlDefNode the aml to be loaded. This can be a string or a parsed piece of xml. + * @param {HTMLElement} oInt the html parent of the created aml elements. + */ + this.replaceMarkup = function(amlDefNode, options) { + + + if (!options) + options = {}; + + if (!options.$intAML) + options.$intAML = this.$aml; + if (!options.$int) + options.$int = this.$int; + options.clear = true; + + //Remove All the childNodes + for (var i = this.childNodes.length - 1; i >= 0; i--) { + var oItem = this.childNodes[i]; + /*var nodes = oItem.childNodes; + for (var k = 0; k < nodes.length; k++) + if (nodes[k].destroy) + nodes[k].destroy(true); + + if (oItem.$aml && oItem.$aml.parentNode) + oItem.$aml.parentNode.removeChild(oItem.$aml);*/ + + if (oItem.destroy) + oItem.destroy(true); + + if (oItem.$ext != this.$int) + apf.destroyHtmlNode(oItem.$ext); + } + + this.childNodes.length = 0; + + this.$int.innerHTML = "
loading...
"; + + //Do an insertMarkup + this.insertMarkup(amlDefNode, options); + }; + + /** + * Inserts new aml into this element. + * @param {mixed} amlDefNode the aml to be loaded. This can be a string or a parsed piece of xml. + * @param {Object} options + * Properties: + * callback + * clear + */ + this.insertMarkup = function(amlDefNode, options){ + + + + + var _self = this; + var include = new apf.XiInclude(); + + if (amlDefNode.trim().charAt(0) == "<") + amlDefNode = apf.getXml(amlDefNode); + + include.setAttribute("href", amlDefNode); + if (options && options.clear) + include.setAttribute("clear", true); + include.options = options; + include.callback = options && options.callback || function(){ + _self.dispatchEvent("afteramlinserted", {src: amlDefNode}); + }; + this.appendChild(include); + }; + + //@todo prefix only needs on top element + this.serialize = function(shallow){ + if (shallow || !this.firstChild) { + return "<" + + (this.prefix + ? this.prefix + ":" + this.localName + " xmlns:" + + this.prefix + "=\"" + this.namespaceURI + "\"" + : this.localName) + (this.attributes && this.attributes.length ? " " : "") + + (this.attributes && this.attributes.join(" ") || "") + + "/>"; + } + else { + var str = ["<" + + (this.prefix + ? this.prefix + ":" + this.localName + " xmlns:" + + this.prefix + "=\"" + this.namespaceURI + "\"" + : this.localName) + (this.attributes && this.attributes.length ? " " : "") + + (this.attributes && this.attributes.join(" ") || "") + + ">"]; + + for (var i = this.firstChild; i; i = i.nextSibling) + str.push(i.serialize()); + + return str.join("") + ""; + } + }; + + this.$setInheritedAttribute = function(prop){ + var value, node = this, isInherit = false; + + value = node.getAttribute(prop); + if (!value) { + node = node.parentNode; + + //Second argument fetches special inheritance value, if any + while (node && node.nodeType == 1 && !(value = node.getAttribute(prop, true))) { + node = node.parentNode; + } + + isInherit = true; + } + + if (!value && apf.config && prop) + value = apf.config[prop]; + + if (value) { + + //Remove any bounds if relevant + this.$clearDynamicProperty(prop); + + if (isInherit) + this.$inheritProperties[prop] = 2; + + if (typeof value == "string" + && (value.indexOf("{") > -1 || value.indexOf("[") > -1)) { + this.$setDynamicProperty(prop, value); + } + else + + this.setProperty(prop, value, false, false, 2); + } + + return value; + }; + + //@todo in proper W3C implementation this needs to change + //@todo this won't work with a combo of remove/append + this.addEventListener("DOMNodeInserted", function(e){ + if (e.currentTarget != this || e.$isMoveWithinParent || !e.$oldParent) + return; + + //Check inherited attributes for reparenting + /* + States: + -1 Set + undefined Pass through + 2 Inherited + 3 Semi-inherited + 10 Dynamic property + */ + var vOld, vNew; + var aci = apf.config.$inheritProperties; + for (var prop in aci) { + vOld = apf.getInheritedAttribute(e.$oldParent, prop); + vNew = apf.getInheritedAttribute(this.parentNode, prop); + + //Property has changed, lets recursively set it on inherited nodes + if (vOld != vNew) { + //@todo code duplication from class.js + (function recur(nodes) { + var i, l, node, n; + for (i = 0, l = nodes.length; i < l; i++) { + node = nodes[i]; + if (node.nodeType != 1 && node.nodeType != 7) + continue; + + //Pass through + n = node.$inheritProperties[prop]; + if (aci[prop] == 1 && !n) + recur(node.childNodes); + + //Set inherited property + //@todo why are dynamic properties overwritten?? + else if(!(n < 0)) {//Will also pass through undefined - but why??? @todo seems inefficient + if (n == 3) { + var sameValue = node[prop]; + node[prop] = null; + } + node.setProperty(prop, n != 3 + ? vNew + : sameValue, false, false, n); //This is recursive already + } + } + })([this]); + } + } + }); + + this.$handlePropSet = function(prop, value, force){ + if (this.$booleanProperties[prop]) + value = apf.isTrue(value); + + + + this[prop] = value; + + var handler; + return (handler = this.$propHandlers && this.$propHandlers[prop] + || this.nodeFunc == apf.NODE_VISIBLE && apf.GuiElement && apf.GuiElement.propHandlers[prop] || null) + && handler.call(this, value, prop, force); + }; + + //var aci = apf.config.$inheritProperties; << UNUSED + this.addEventListener("DOMNodeInsertedIntoDocument", function(e){ + var a, i, l, attr = this.attributes; + + + + + + //Get defaults from the defaults element if it exists + var defs = apf.nameserver.getAll("defaults_" + this.localName); + if (defs.length) { + for (var j = 0, jl = defs.length; j < jl; j++) { + var d = defs[j].attributes, di; + for (i = 0, l = d.length; i < l; i++) { + a = attr.getNamedItem((di = d[i]).nodeName); + if (a) { + if (a.value)//specified + continue; + + a.$setValue(di.nodeValue); + this.$inheritProperties[di.nodeName] = 2; + } + else { + this.attributes.push( + new apf.AmlAttr(this, di.nodeName, di.nodeValue)); + this.$inheritProperties[di.nodeName] = 2; + } + } + } + } + + + + //Set all attributes + for (i = 0, l = attr.length; i < l; i++) { + attr[i].dispatchEvent("DOMNodeInsertedIntoDocument"); + } + }, true); + + this.addEventListener("DOMNodeInsertedIntoDocument", function(e){ + this.$amlLoaded = true; + }); +}).call(apf.AmlElement.prototype = new apf.AmlNode()); + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/core/markup/aml/characterdata.js)SIZE(2018)TIME(Fri, 14 Oct 2011 08:01:44 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + +//@todo apf3.0 The functions seem to not set nodeValue... +apf.AmlCharacterData = function(){ + this.data = ""; + this.length = 0; + + this.$init(true); + + this.appendData = function(sValue){ + this.dispatchEvent("DOMCharacterDataModified", { + value : sValue + }); + }; + + this.deleteData = function(nOffset, nCount){ + this.dispatchEvent("DOMCharacterDataModified", { + offset: nOffset, + count : nCount + }); + }; + + this.insertData = function(nOffset, nCount){ + this.dispatchEvent("DOMCharacterDataModified", { + offset: nOffset, + count : nCount + }); + }; + + this.replaceData = function(nOffset, nCount, sValue){ + this.dispatchEvent("DOMCharacterDataModified", { + offset: nOffset, + count : nCount, + value : sValue + }); + }; + + this.substringData = function(nOffset, nCount){}; +} +apf.AmlCharacterData.prototype = new apf.AmlNode(); + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/core/markup/aml/text.js)SIZE(4136)TIME(Fri, 14 Oct 2011 08:01:44 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + +apf.AmlText = function(isPrototype){ + this.$init(isPrototype); +}; + +(function(){ + this.nodeType = this.NODE_TEXT; + this.nodeName = "#text"; + + this.serialize = function(){ + return apf.xmlentities(this.nodeValue).replace(//g, ">"); + }; + + + + //@todo think about using this.replaceData(); + this.$setValue = function(value){ + //if (!this.$amlLoaded) + //return; + + this.dispatchEvent("DOMCharacterDataModified", { + bubbles : true, + prevValue : this.nodeValue, + newValue : this.nodeValue = value + }); + + if (this.$amlLoaded && this.$ext) + this.$ext.nodeValue = value; + } + + this.addEventListener("DOMNodeInsertedIntoDocument", function(e){ + var pHtmlNode; + if (!(pHtmlNode = this.parentNode.$int) || this.parentNode.hasFeature(apf.__CHILDVALUE__)) + return; + + this.$amlLoaded = true; + + var nodeValue = this.nodeValue; + + //@todo optimize for inside elements? + if (apf.config.liveText && !this.parentNode.hasFeature(apf.__CHILDVALUE__) + && (nodeValue.indexOf("{") > -1 || nodeValue.indexOf("[") > -1)) { + + //Convert to live markup pi + this.$supportedProperties = []; + this.$propHandlers = {}; + this.$booleanProperties = {}; + this.$inheritProperties = {}; + + this.$propHandlers["calcdata"] = apf.LiveMarkupPi.prototype.$propHandlers["calcdata"]; + + this.$setInheritedAttribute = apf.AmlElement.prototype.$setInheritedAttribute; + + this.implement(apf.StandardBinding); + + + pHtmlNode.appendChild(this.$ext = document.createElement("span")); + this.$setDynamicProperty("calcdata", this.nodeValue); + + return; + } + + if (apf.hasTextNodeWhiteSpaceBug) { + var nodeValue = nodeValue.replace(/[\t\n\r ]+/g, " "); + + if (nodeValue && nodeValue != " ") + this.$ext = pHtmlNode.appendChild( + pHtmlNode.ownerDocument.createTextNode(nodeValue)); + } + else + this.$ext = pHtmlNode.appendChild( + pHtmlNode.ownerDocument.createTextNode(nodeValue)); + }, true); +}).call(apf.AmlText.prototype = new apf.AmlCharacterData()); + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/core/markup/aml/attr.js)SIZE(4716)TIME(Fri, 14 Oct 2011 08:01:44 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + +apf.AmlAttr = function(ownerElement, name, value){ + this.$init(); + + if (ownerElement) { + this.ownerElement = ownerElement; + this.ownerDocument = ownerElement.ownerDocument; + } + + this.nodeName = this.name = name; + this.nodeValue = this.value = value; +}; + +(function(){ + this.nodeType = this.NODE_ATTRIBUTE; + + this.MODIFICATION = 1; + this.ADDITION = 2; + this.REMOVAL = 3; + + this.serialize = + this.toString = function(){ + return this.name + "=\"" + apf.xmlentities(String(this.value)) + .replace(//g, ">") + "\""; + }; + + + + this.$setValue = function(value){ + this.nodeValue = this.value = value; + this.specified = true; + + //@todo apf3.0 domattr + this.ownerElement.dispatchEvent("DOMAttrModified", { + relatedNode : this, + attrChange : this.MODIFICATION, + attrName : this.name, + newValue : value, + prevValue : this.$lastValue || "", + bubbles : true + }); + + this.$lastValue = value; + }; + + this.$triggerUpdate = function(e, oldValue){ + var name = this.name, + value = this.value || this.nodeValue, + host = this.ownerElement; + + if (name == "id" && !this.specified && host.id) { + this.specified = true; + return; + } + + if (name.substr(0, 2) == "on") { + if (host.$events[name]) + host.removeEventListener(name.replace(/^on/, ""), host.$events[name]); + if (value) + host.addEventListener(name, (host.$events[name] = + (typeof value == "string" + ? + apf.lm.compile(value, {event: true, parsecode: true}) + + : value))); + return; + } + + if (this.specified) + host.$clearDynamicProperty(name); + + if (typeof value == "string" && (host.$attrExcludePropBind[name] || + (value.indexOf("{") > -1 || value.indexOf("[") > -1))) + host.$setDynamicProperty(name, value); + else + + { + host.setProperty(name, value); //@todo apf3.0 is this a lot slower? + } + //host.$handlePropSet(name, value); + + if (this.specified) { + //@todo apf3.0 domattr - slow? + host.dispatchEvent("DOMAttrModified", { //@todo this is not good, node might not be specified at init + relatedNode : this, + attrChange : this.MODIFICATION, + attrName : name, + newValue : value, + prevValue : this.$lastValue || "", + bubbles : true + }); + } + else this.specified = true; + + this.$lastValue = value; + }; + + //@todo apf3.0 domattr + this.addEventListener("DOMNodeInsertedIntoDocument", this.$triggerUpdate); +}).call(apf.AmlAttr.prototype = new apf.AmlNode()); + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/core/markup/aml/cdatasection.js)SIZE(1300)TIME(Fri, 14 Oct 2011 08:01:44 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + +apf.AmlCDATASection = function(isPrototype){ + this.nodeType = this.NODE_CDATA_SECTION; + this.nodeName = "#cdata-section"; + + this.$init(isPrototype); +}; + +apf.AmlCDATASection.prototype = new apf.AmlText(true); +apf.AmlCDATASection.prototype.serialize = function(){ + return ""; +}; + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/core/markup/aml/comment.js)SIZE(1509)TIME(Fri, 14 Oct 2011 08:01:44 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + +apf.AmlComment = function(isPrototype){ + this.nodeType = this.NODE_COMMENT; + this.nodeName = "#comment"; + + this.$init(isPrototype); +}; + +(function(){ + this.serialize = function(){ + return ""; + }; + + this.$setValue = function(value){ + this.dispatchEvent("DOMCharacterDataModified", { + bubbles : true, + newValue : value, + prevValue : this.nodeValue + }); + } +}).call(apf.AmlComment.prototype = new apf.AmlCharacterData()); + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/core/markup/aml/configuration.js)SIZE(1384)TIME(Fri, 14 Oct 2011 08:01:44 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + +apf.AmlConfiguration = function(isPrototype){ + this.parameterNames = []; + + this.$init(isPrototype); +}; + +(function(){ + this.setParameter = this.setProperty; + + this.getParameter = this.getProperty; + + this.canSetParameter = function(name, value){ //@todo for value + return this.parameterNames.indexOf(name) > -1; + }; +}).call(apf.AmlConfiguration.prototype = new apf.Class()); + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/core/markup/aml/document.js)SIZE(9454)TIME(Fri, 14 Oct 2011 08:01:44 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + + +/** + * The aml document, this is the root of the DOM Tree and has a nodeType with + * value 9 (apf.NODE_DOCUMENT). + * + * @constructor + * @inherits apf.AmlNode + * @inherits apf.Class + * @default_private + * @see baseclass.amldom + * + * @author Ruben Daniels (ruben AT ajax DOT org) + * @version %I%, %G% + * @since 0.8 + */ +apf.AmlDocument = function(){ + this.$prefixes = {}; + this.$namespaceURIs = {}; + this.domConfig = new apf.AmlConfiguration(); + + this.$init(); +}; + +(function() { + /** + * The type of node within the document. + * Possible values: + */ + this.nodeType = this.NODE_DOCUMENT; + this.nodeFunc = apf.NODE_HIDDEN; + this.nodeName = "#document"; + + this.$amlLoaded = true; + + this.activeElement = null; //@todo alias of window.foccussed; + this.doctype = null; + this.domConfig = null; + this.implementation = null; + this.characterSet = apf.characterSet; + + /** + * The root element node of the aml application. This is an element with + * the tagName 'application'. This is similar to the 'html' element + */ + this.documentElement = null; + + /** + * Gets a aml element based on it's id. + * @param {String} id the id of the aml element to return. + * @return {AMLElement} the aml element with the id specified. + */ + this.getElementById = function(id){ + return self[id]; + }; + + this.getElementsByTagName = function(tagName){ + var docEl, res = (docEl = this.documentElement) + .getElementsByTagName(tagName); + + if (tagName == "*" || docEl.tagName == tagName) + res.unshift(docEl); + return res; + }; + + this.getElementsByTagNameNS = function(nameSpaceURI, tagName){ + var docEl, + res = (docEl = this.documentElement) + .getElementsByTagNameNS(nameSpaceURI, tagName); + + if (tagName == "*" || docEl.tagName == tagName && docEl.namespaceURI == nameSpaceURI) + res.unshift(docEl); + return res; + }; + + /** + * Creates a new aml element. + * @param {mixed} tagName information about the new node to create. + * Possible values: + * {String} the tagName of the new element to create + * {String} the aml definition for a single or multiple elements. + * {XMLElement} the aml definition for a single or multiple elements. + * @return {AMLElement} the created aml element. + */ + this.createElement = function(qualifiedName){ + return this.$domParser.$createNode(this, this.NODE_ELEMENT, null, + this.namespaceURI, qualifiedName); + }; + + this.createElementNS = function(namespaceURI, qualifiedName){ + return this.$domParser.$createNode(this, this.NODE_ELEMENT, null, + namespaceURI, qualifiedName); + }; + + this.importNode = function(node, deep){ + if (deep && node.nodeType == 1) { + return this.$domParser.parseFromXml(node, { + doc : this, + delay : true + }).childNodes[0]; + } + else { + return this.$domParser.$createNode(this, node.nodeType, node); + } + }; + + //@todo + this.createAttribute = function(nodeName){ + return this.$domParser.$createNode(this, this.NODE_ATTRIBUTE, null, + this.nameSpaceURI, nodeName); + }; + + //@todo + this.createAttributeNS = function(nameSpaceURI, nodeName){ + return this.$domParser.$createNode(this, this.NODE_ATTRIBUTE, null, + nameSpaceURI, nodeName); + }; + + this.createEvent = function(){ + return new apf.AmlEvent(); + }; + + this.createComment = function(nodeValue){ + return this.$domParser.$createNode(this, this.NODE_COMMENT, null, null, + null, nodeValue); + }; + + this.createProcessingInstruction = function(target, data){ + return this.$domParser.$createNode(this, this.NODE_PROCESSING_INSTRUCTION, + null, null, target, data); + }; + + this.createCDATASection = function(nodeValue){ + return this.$domParser.$createNode(this, this.NODE_CDATA_SECTION, null, + null, null, nodeValue); + }; + + this.createTextNode = function(nodeValue){ + return this.$domParser.$createNode(this, this.NODE_TEXT, null, null, + null, nodeValue); + }; + + this.createDocumentFragment = function(){ + return this.$domParser.$createNode(this, this.NODE_DOCUMENT_FRAGMENT); + }; + + this.querySelector = function(){}; + + this.querySelectorAll = function(){}; + + + /** + * See W3C evaluate + */ + this.evaluate = function(sExpr, contextNode, nsResolver, type, x){ + var result = apf.XPath.selectNodes(sExpr, + contextNode || this.documentElement); + + /** + * @private + */ + return { + snapshotLength : result.length, + snapshotItem : function(i){ + return result[i]; + } + } + }; + + /** + * See W3C createNSResolver + */ + this.createNSResolver = function(contextNode){ + return {}; + }; + + + this.hasFocus = function(){ + + } + + +}).call(apf.AmlDocument.prototype = new apf.AmlNode()); + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/core/markup/aml/documentfragment.js)SIZE(1286)TIME(Fri, 14 Oct 2011 08:01:44 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + +apf.AmlDocumentFragment = function(isPrototype){ + this.$init(isPrototype); +}; + +apf.AmlDocumentFragment.prototype = new apf.AmlNode(); +apf.AmlDocumentFragment.prototype.nodeName = "#document-fragment"; +apf.AmlDocumentFragment.prototype.nodeType = + apf.AmlDocumentFragment.prototype.NODE_DOCUMENT_FRAGMENT; + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/core/markup/aml/event.js)SIZE(2086)TIME(Fri, 14 Oct 2011 08:01:44 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + +/** + * Implementation of W3C event object. An instance of this class is passed as + * the first argument of any event handler. Per event it will contain different + * properties giving context based information about the event. + * @constructor + * @default_private + */ +apf.AmlEvent = function(name, data){ + this.name = name; + + var prop; + for (prop in data) + this[prop] = data[prop]; +}; + +apf.AmlEvent.prototype = { + + bubbles : false, + cancelBubble : false, + + + /** + * Cancels the event if it is cancelable, without stopping further + * propagation of the event. + */ + preventDefault : function(){ + this.returnValue = false; + }, + + + /** + * Prevents further propagation of the current event. + */ + stopPropagation : function(){ + this.cancelBubble = true; + }, + + + stop : function() { + this.returnValue = false; + + this.cancelBubble = true; + + } +}; + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/core/markup/aml/namednodemap.js)SIZE(3407)TIME(Fri, 14 Oct 2011 08:01:44 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + +//@todo apf3.0 +apf.AmlNamedNodeMap = function(host){ + this.$host = host; +}; + +(function(){ + this.getNamedItem = function(name){ + for (var i = 0; i < this.length; i++) { + if (this[i].name == name) + return this[i]; + } + return false; + }; + + this.setNamedItem = function(node){ + var name = node.name; + for (var item, i = this.length - 1; i >= 0; i--) { + if (this[i].name == name) { + this[i].ownerElement = null; + this.splice(i, 1); + break; + } + } + + this.push(node); + + node.ownerElement = this.$host; + node.ownerDocument = this.$host.ownerDocument; + node.$triggerUpdate(); + }; + + //@todo apf3.0 domattr + this.removeNamedItem = function(name){ + //Should deconstruct dynamic properties + + for (var item, i = this.length - 1; i >= 0; i--) { + if (this[i].name == name) { + item = this[i]; + this.splice(i, 1); + break; + } + } + if (!item) return false; + + //@todo hack! + //this should be done properly + var oldValue = item.nodeValue; + item.nodeValue = item.value = ""; + item.$triggerUpdate(null, oldValue); + item.ownerElement = null; + item.nodeValue = item.value = oldValue; + + return item; + }; + + this.item = function(i){ + return this[i]; + }; + + //if (apf.isIE < 8) { //Only supported by IE8 and above + this.length = 0; + + this.splice = function(pos, length){ + for (var i = pos, l = this.length - length; i < l; i++) { + this[i] = this[i + 1]; + } + delete this[i]; + this.length -= length; + } + + this.push = function(o) { + this[this.length++] = o; + return this.length; + } + //} + + this.join = function(glue){ + var x = []; + for (var e, a, i = 0, l = this.length; i < l; i++) { + if ((e = (a = this[i]).ownerElement) && e.$inheritProperties[a.nodeName] != 2) + x.push(this[i]); + } + return x.join(glue); + } +}).call(apf.AmlNamedNodeMap.prototype = {}); //apf.isIE < 8 ? {} : [] + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/core/markup/aml/processinginstruction.js)SIZE(4311)TIME(Fri, 14 Oct 2011 08:01:44 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + +apf.AmlProcessingInstruction = function(isPrototype){ + this.$init(isPrototype); +}; + +(function(){ + this.nodeType = this.NODE_PROCESSING_INSTRUCTION; + + /** + * @todo docs + */ + this.data = null; + + /** + * @todo docs + */ + this.target = null; + + this.serialize = function(){ + return ""; + }; + + this.reload = function(){ + this.$handlePropSet("data", this.data); + }; + + //1 = force no bind rule, 2 = force bind rule + this.$attrExcludePropBind = apf.extend({ + calcdata : 0 //Start in code mode + }, this.$attrExcludePropBind); + + this.getAttribute = function(){}; + this.$setInheritedAttribute = apf.AmlElement.prototype.$setInheritedAttribute; + this.$supportedProperties = []; + this.$propHandlers = {}; + this.$booleanProperties = {}; + this.$inheritProperties = {}; + + + + this.$setValue = function(value){ + this.setProperty("data", value); + }; + + this.$handlePropSet = function(prop, value, force){ + this[prop] = value; + if (prop == "data") { + this.$clearDynamicProperty("calcdata"); + this.$setDynamicProperty("calcdata", value); + } + + else if (prop == "target") { + //not implemented + } + else if (this.$propHandlers[prop]) { + this.$propHandlers[prop].call(this, value, prop); + } + }; + + this.addEventListener("DOMNodeInsertedIntoDocument", function(e){ + var pHtmlNode = e.pHtmlNode; + if (!pHtmlNode && (this.parentNode.$bindingRule + || !(pHtmlNode = this.parentNode.$int))) + return; + + pHtmlNode.appendChild(this.$ext = document.createElement("span")); + this.$ext.host = this; + + + + this.$setDynamicProperty("calcdata", this.data); + + + }, true); + + /*this.addEventListener("DOMNodeRemovedFromDocument", function(e){ + this.$clearDynamicProperty("calcdata"); + });*/ + + this.$destroy = function(){ + this.$clearDynamicProperty("calcdata"); + this.$propHandlers["calcdata"].call(this, ""); + }; +}).call(apf.AmlProcessingInstruction.prototype = new apf.AmlNode()); + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/core/markup/aml/range.js)SIZE(15809)TIME(Fri, 14 Oct 2011 08:01:44 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/core/markup/aml/selection.js)SIZE(8861)TIME(Fri, 14 Oct 2011 08:01:44 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + +apf.AmlSelection = function(doc){ + this.$init(); + + this.$ownerDocument = doc; + + var _self = this; + var monitor = false; + this.$monitorRange = function(){ + apf.queue.add("selrange" + this.$uniqueId, function(){ + var range = _self.$ranges[0]; + _self.anchorNode = range.startContainer; + _self.anchorOffset = range.startOffset; + + range = _self.$ranges[_self.rangeCount - 1]; + _self.focusNode = range.endContainer; + _self.focusOffset = range.endOffset; + monitor = false; + }); + monitor = true; + } + + var update = false; + this.$update = function(){ + if (!update) { + apf.queue.add("selupdate" + this.$uniqueId, function(){ + _self.dispatchEvent("update"); + update = false; + }); + update = true; + } + } +}; +(function() { + /** + * Returns the element that contains the start of the selection. + * Returns null if there's no selection. + */ + this.anchorNode = null; + + /** + * Returns the offset of the start of the selection relative to the element that contains the start of the selection. + * Returns 0 if there's no selection. + */ + this.anchorOffset = 0; + + /** + * Returns the element that contains the end of the selection. + * Returns null if there's no selection. + */ + this.focusNode = null; + + /** + * Returns the offset of the end of the selection relative to the element that contains the end of the selection. + *Returns 0 if there's no selection. + */ + this.focusOffset = null; + + /** + * Returns the number of ranges in the selection. + */ + this.rangeCount = 0; + this.$ranges = []; + + this.toString = function(){ + return "[apf.AmlSelection]";// this.$ranges.join(""); + } + + /** + * Returns true if there's no selection or if the selection is empty. Otherwise, returns false. + */ + this.isCollapsed = function(){ + return !this.rangeCount || this.rangeCount == 1 && this.$ranges[0].collapsed; + } + + /** + * Replaces the selection with an empty one at the given position. + */ + this.collapse = function(parentNode, offset, noEvent){ + for (var i = 0, l = this.$ranges.length; i < l; i++) { + (range = this.$ranges[i]).removeEventListener("update", this.$monitorRange); + range.detach(); + } + + var range; + this.$ranges = [range = new apf.AmlRange(this.ownerDocument)]; + range.addEventListener("update", this.$monitorRange); + range.setStart(parentNode, offset); + range.setEnd(parentNode, offset); + this.rangeCount = 1; + + this.focusNode = + this.anchorNode = parentNode; + this.anchorOffset = + this.focusOffset = offset; + + if (!noEvent) + this.dispatchEvent("update"); + } + + /** + * Replaces the selection with an empty one at the position of the start of the current selection. + */ + this.collapseToStart = function(){ + + + var range = this.$ranges[0]; + this.collapse(range.startContainer, range.startOffset); + } + + /** + * Replaces the selection with an empty one at the position of the end of the current selection. + */ + this.collapseToEnd = function(){ + + + var range = this.$ranges[this.rangeCount - 1]; + this.collapse(range.endContainer, range.endOffset); + } + + /** + * Replaces the selection with one that contains all the contents of the given element. + */ + this.selectAllChildren = function(parentNode){ + this.collapse(parentNode, 0, true); + var range = this.$ranges[0]; + range.selectNodeContents(parseNode); + + this.focusNode = + this.anchorNode = parentNode; + this.anchorOffset = 0; + this.focusOffset = range.endOffset; + + this.$update(); + } + + /** + * Deletes the selection. + */ + this.deleteFromDocument = function(){ + for (var i = 0, l = this.$ranges.length; i < l; i++) { + this.$ranges[i].deleteContents(); + } + + var range = this.$ranges[0]; + this.anchorNode = range.startContainer; + this.anchorOffset = range.startOffset; + + range = this.$ranges[this.rangeCount - 1]; + this.focusNode = range.endContainer; + this.focusOffset = range.endOffset; + + this.$update(); + } + + /** + * Returns the given range. + */ + this.getRangeAt = function(index){ + + + return this.$ranges[index]; + } + + /** + * Adds the given range to the selection. + */ + this.addRange = function(range){ + this.rangeCount = this.$ranges.push(range); + + this.focusNode = range.endContainer; + this.focusOffset = range.endOffset; + range.addEventListener("update", this.$monitorRange); + + this.$update(); + + return range; + } + + /** + * Removes the given range from the selection, if the range was one of the ones in the selection. + */ + this.removeRange = function(range){ + this.$ranges.remove(range); + this.rangeCount = this.$ranges.length; + range.removeEventListener("update", this.$monitorRange); + + var range = this.$ranges[0]; + this.anchorNode = range.startContainer; + this.anchorOffset = range.startOffset; + + range = this.$ranges[this.rangeCount - 1]; + this.focusNode = range.endContainer; + this.focusOffset = range.endOffset; + + this.$update(); + } + + /** + * Removes all the ranges in the selection. + */ + this.removeAllRanges = function(){ + for (var range, i = 0, l = this.$ranges.length; i < l; i++) { + (range = this.$ranges[i]).removeEventListener("update", this.$monitorRange); + range.detach(); + } + + this.$ranges = []; + this.rangeCount = 0; + + this.anchorNode = null; + this.anchorOffset = 0; + this.focusNode = null; + this.focusOffset = 0; + + this.$update(); + } + + //currently only ranges with single element selected is supported + this.$getNodeList = function(){ + var nodes = []; + for (var r, i = 0; i < this.rangeCount; i++) { + nodes.push((r = this.$ranges[i]).startContainer.childNodes[r.startOffset]); + } + return nodes; + } + + this.$selectList = function(list){ + this.removeAllRanges(); + for (var i = 0; i < list.length; i++) { + this.addRange(new apf.AmlRange(this)).selectNode(list[i]); + } + } + + this.$getFirstNode = function(){ + var r; + return (r = this.$ranges[0]).startContainer.childNodes[r.startOffset]; + } +}).call(apf.AmlSelection.prototype = new apf.Class()); + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/core/markup/aml/textrectangle.js)SIZE(1662)TIME(Fri, 14 Oct 2011 08:01:44 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + +apf.AmlTextRectangle = function(host){ + var _self = this; + function handler(){ + var pos = _self.getAbsolutePosition(_self.$ext); + _self.setProperty("left", pos[0]); + _self.setProperty("top", pos[1]); + _self.setProperty("right", document.documentElement.offsetWidth - pos[0]); + _self.setProperty("bottom", document.documentElement.offsetWidth - pos[1]); + } + + host.addEventListener("prop.width", handler); + host.addEventListener("prop.height", handler); + host.addEventListener("prop.left", handler); + host.addEventListener("prop.top", handler); + + handler.call(host); +}; +apf.AmlTextRectangle.prototype = new apf.Class(); + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/core/markup/xhtml.js)SIZE(1530)TIME(Fri, 14 Oct 2011 08:01:44 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + + +/** + * Object creating the XHTML namespace for the aml parser. + * + * @constructor + * @parser + * + * @author Ruben Daniels (ruben AT ajax DOT org) + * @version %I%, %G% + * @since 0.8 + */ +apf.xhtml = new apf.AmlNamespace(); +apf.setNamespace("http://www.w3.org/1999/xhtml", apf.xhtml); + + +/* +if (apf.getTextNode(x)) { + var data = { + amlNode : x, + htmlNode : o + } + + +} + +*/ + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/core/markup/xhtml/element.js)SIZE(5022)TIME(Fri, 14 Oct 2011 08:01:44 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + + +apf.XhtmlElement = function(struct, tagName){ + this.$init(tagName || true, apf.NODE_VISIBLE, struct); + + this.$xoe = this.addEventListener; + this.addEventListener = this.$xae; + this.removeEventListener = this.$xre; + + var _self = this; + this.$de = function(e){ + _self.dispatchEvent(e.type, null, e); + } +}; + +(function(){ + var excludedEvents = { + "contextmenu": 1, + "keydown": 1, + "keypress": 1, + "keyup": 1, + "DOMNodeInserted": 2, + "DOMNodeInsertedIntoDocument": 2, + "DOMNodeRemoved": 2, + "DOMNodeRemovedFromDocument": 2 + }; + + this.$xae = function(type, fn){ + this.$xoe.apply(this, arguments); + + if (excludedEvents[type] > (this.editable ? 0 : 1) + || type.substr(0, 5) == "prop.") + return; + + if (this.$ext) { + if (type.substr(0,2) == "on") + type = type.substr(2); + apf.addListener(this.$ext, type, this.$de); + } + }; + + this.$xre = function(type, fn) { + apf.AmlElement.prototype.removeEventListener.apply(this, arguments); + + + + if (this.$ext) + apf.removeListener(this.$ext, type, this.$de); + } + + this.$handlePropSet = function(name, value, force, inherit){ + if (this.$booleanProperties[name]) + value = apf.isTrue(value); + + this[name] = value; + var handler = this.$propHandlers && this.$propHandlers[name] + || apf.GuiElement.propHandlers[name]; + + if (handler) + handler.call(this, value, null, name); + else if (this.$int && (force || this.$amlLoaded)) { + this.$int.setAttribute(apf.isIE && apf.isIE < 8 && name == "class" + ? "className" : name, value); + } + }; + + this.addEventListener("DOMNodeInsertedIntoDocument", function(e){ + var pHtmlNode; + if (!(pHtmlNode = this.$pHtmlNode = this.parentNode.$int)) + return; + + var str, aml = this.$aml; + if (aml) { + if (aml.serialize) + str = aml.serialize(); + else { + aml = aml.cloneNode(false); + str = aml.xml || aml.nodeValue; + } + + str = str.replace(/ on\w+="[^"]*"| on\w+='[^']*'/g, ""); + + this.$ext = + this.$int = apf.insertHtmlNode(null, pHtmlNode, null, apf.html_entity_decode(str)); + } + else { + this.$ext = this.$int = + pHtmlNode.appendChild(document.createElement(this.localName)); + } + + if (this.localName != "a") + this.$ext.host = this; + + this.style = this.$ext.style; + }, true); + + + this.addEventListener("DOMNodeInsertedIntoDocument", function(e){ + this.$amlLoaded = true; + + if (this.$setLayout) + this.$setLayout(); + }); + +}).call(apf.XhtmlElement.prototype = new apf.AmlElement()); + +apf.Init.addConditional(function(){ + if (apf.isO3) return; + var prot = apf.XhtmlElement.prototype; + + //prot.implement(apf.Interactive); + prot.implement( + + apf.Anchoring + + ); + + + prot.$drawn = true; + prot.$setLayout = apf.GuiElement.prototype.$setLayout; + + prot.addEventListener("DOMNodeInserted", function(e){ + if (e.currentTarget == this + && "vbox|hbox|table".indexOf(this.parentNode.localName) == -1) { + this.$setLayout(); + } + }); + +}, null, ["interactive"]); + +apf.xhtml.setElement("@default", apf.XhtmlElement); + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/core/markup/xhtml/body.js)SIZE(1783)TIME(Fri, 14 Oct 2011 08:01:44 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + +apf.XhtmlBodyElement = function(struct, tagName){ + this.$init(tagName || "body", apf.NODE_VISIBLE, struct); +}; + +(function(){ + + + this.addEventListener("DOMNodeInsertedIntoDocument", function(e){ + if (!this.ownerDocument.body) + this.ownerDocument.body = this; + + this.$ext = + this.$int = document.body; + }, true); +}).call(apf.XhtmlBodyElement.prototype = new apf.AmlElement()); + +apf.Init.addConditional(function(){ + if (apf.isO3) return; + var prot = apf.XhtmlBodyElement.prototype; + + +}, null, ["interactive"]); + +apf.xhtml.setElement("body", apf.XhtmlBodyElement); + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/core/markup/xhtml/html.js)SIZE(2693)TIME(Fri, 14 Oct 2011 08:01:44 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + + +/** + * @todo description + * + * @author Ruben Daniels (ruben AT ajax DOT org) + * @version %I%, %G% + * @since 0.4 + */ +apf.XhtmlHtmlElement = function(struct, tagName){ + this.$init(tagName || "html", apf.NODE_VISIBLE, struct); + + + + this.$ext = document.documentElement; + this.$ext.host = this; + + this.$int = document.body; + this.$tabList = []; //Prevents documentElement from being focussed + this.$focussable = apf.KEYBOARD; + this.focussable = true; + this.visible = true; + this.$isWindowContainer = true; + //this.focus = function(){ this.dispatchEvent("focus"); }; + //this.blur = function(){ this.dispatchEvent("blur"); }; + + this.implement(apf.Focussable); + + + apf.window.$addFocus(this); + + + this.addEventListener("DOMNodeInsertedIntoDocument", function(e){ + var i, l, n, a, c, + attr = this.attributes, doc = this.ownerDocument; + for (i = 0, l = attr.length; i < l; i++) { + n = (a = attr[i]).nodeName.split(":"); + if (n[0] == "xmlns") { + if (c = n[1]) { + doc.$prefixes[c] = a.nodeValue; + doc.$namespaceURIs[a.nodeValue] = c; + } + else { + doc.namespaceURI = a.nodeValue; + } + } + } + + if (!doc.namespaceURI) + doc.namespaceURI = apf.ns.xhtml; + }); +}; +apf.XhtmlHtmlElement.prototype = new apf.XhtmlElement(); + +apf.xhtml.setElement("html", apf.XhtmlHtmlElement); + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/core/markup/xhtml/ignore.js)SIZE(1360)TIME(Fri, 14 Oct 2011 08:01:44 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + +apf.XhtmlIgnoreElement = function(struct, tagName){ + this.$init(tagName, apf.NODE_VISIBLE, struct); +}; + +apf.XhtmlIgnoreElement.prototype = new apf.AmlElement(); + +apf.xhtml.setElement("script", apf.XhtmlIgnoreElement); +apf.xhtml.setElement("noscript", apf.XhtmlIgnoreElement); +apf.xhtml.setElement("head", apf.XhtmlIgnoreElement); +apf.xhtml.setElement("meta", apf.XhtmlIgnoreElement); + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/core/markup/xhtml/input.js)SIZE(2187)TIME(Fri, 14 Oct 2011 08:01:44 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + +apf.XhtmlInputElement = function(struct, tagName){ + this.$init(tagName || "input", apf.NODE_VISIBLE, struct); +}; + +(function(){ + this.$xae = apf.XhtmlElement.prototype.$xae; + this.$xre = apf.XhtmlElement.prototype.$xre; + this.$handlePropSet = function(name, value, force){ + if (name == "type") + return; + + return apf.XhtmlElement.prototype.$handlePropSet.call(this, name, value, force); + }; + + this.addEventListener("DOMNodeInsertedIntoDocument", function(e){ + var pHtmlNode; + if (!(pHtmlNode = this.parentNode.$int)) + return; + + if (this.$aml) { + this.$ext = + this.$int = apf.insertHtmlNode(this.$aml.serialize + ? this.$aml + : this.$aml.cloneNode(false), pHtmlNode); + } + else { + this.$ext = this.$int = document.createElement(this.localName); + if (this.getAttribute("type")) + this.$int.setAttribute("type", this.getAttribute("type")); + pHtmlNode.appendChild(this.$int); + } + }, true); +}).call(apf.XhtmlInputElement.prototype = new apf.AmlElement()); + +apf.xhtml.setElement("input", apf.XhtmlInputElement); + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/core/markup/xhtml/option.js)SIZE(1537)TIME(Fri, 14 Oct 2011 08:01:44 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + + +apf.XhtmlOptionElement = function(struct, tagName){ + this.$init(tagName || "option", apf.NODE_VISIBLE, struct); +}; + +(function(){ + this.addEventListener("DOMNodeInsertedIntoDocument", function(e){ + this.$ext = + this.$int = this.parentNode.$int.appendChild( + this.parentNode.$int.ownerDocument.createElement("option")); + + if (this.value) + this.$int.setAttribute("value", this.value); + }, true); +}).call(apf.XhtmlOptionElement.prototype = new apf.AmlElement()); + +apf.xhtml.setElement("option", apf.XhtmlOptionElement); + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/core/markup/xhtml/skipchildren.js)SIZE(2342)TIME(Fri, 14 Oct 2011 08:01:44 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + +apf.XhtmlSkipChildrenElement = function(struct, tagName){ + this.$init(tagName, apf.NODE_VISIBLE, struct); +}; + +(function(){ + this.canHaveChildren = false; + + this.$redraw = function(){ + var _self = this; + apf.queue.add("redraw" + this.$uniqueId, function(){ + var pHtmlNode = _self.$ext.parentNode; + var beforeNode = _self.$ext.nextSibling; + pHtmlNode.removeChild(_self.$ext); + + _self.$ext = apf.insertHtmlNode(null, pHtmlNode, beforeNode, _self.$aml + ? (_self.$aml.serialize ? _self.$aml.serialize() : _self.$aml.xml) + : _self.serialize()); + }); + } + + this.addEventListener("DOMNodeInsertedIntoDocument", function(e){ + var pHtmlNode; + if (!(pHtmlNode = this.parentNode.$int)) + return; + + this.$ext = apf.insertHtmlNode(null, pHtmlNode, null, this.$aml + ? (this.$aml.serialize ? this.$aml.serialize() : this.$aml.xml) + : this.serialize()); + }, true); +}).call(apf.XhtmlSkipChildrenElement.prototype = new apf.AmlElement()); + +apf.xhtml.setElement("object", apf.XhtmlSkipChildrenElement); +apf.xhtml.setElement("embed", apf.XhtmlSkipChildrenElement); +apf.xhtml.setElement("table", apf.XhtmlSkipChildrenElement); + +apf.xhtml.setElement("pre", apf.XhtmlSkipChildrenElement); + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/core/markup/xsd.js)SIZE(12998)TIME(Fri, 14 Oct 2011 08:01:44 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + + +/** + * Object creating the XML Schema namespace for the aml parser. + * + * @constructor + * @parser + * + * @allownode simpleType, complexType + * + * @author Ruben Daniels (ruben AT ajax DOT org) + * @version %I%, %G% + * @since 0.8 + */ +apf.xsd = new apf.AmlNamespace(); +apf.setNamespace("http://www.w3.org/2001/XMLSchema", apf.xsd); + +apf.xsd.typeHandlers = { + "http://www.w3.org/2001/XMLSchema" : { + //XSD datetypes [L10n potential] + "dateTime": function(value){ + value = value.replace(/-/g, "/"); + + value.match(/^(\d{2})\/(\d{2})\/(\d{4}) (\d{2}):(\d{2}):(\d{2})$/); + if (!RegExp.$3 || RegExp.$3.length < 4) + return false; + + var dt = new Date(value); + if (dt.getFullYear() != parseFloat(RegExp.$3)) + return false; + if (dt.getMonth() != parseFloat(RegExp.$2) - 1) + return false; + if (dt.getDate() != parseFloat(RegExp.$1)) + return false; + if (dt.getHours() != parseFloat(RegExp.$4)) + return false; + if (dt.getMinutes() != parseFloat(RegExp.$5)) + return false; + if (dt.getSeconds() != parseFloat(RegExp.$5)) + return false; + + return true; + }, + "time": function(value){ + value.match(/^(\d{2}):(\d{2}):(\d{2})$/); + + var dt = new Date("21/06/1980 " + value); + if (dt.getHours() != parseFloat(RegExp.$1)) + return false; + if (dt.getMinutes() != parseFloat(RegExp.$2)) + return false; + if (dt.getSeconds() != parseFloat(RegExp.$3)) + return false; + + return true; + }, + "date": function(value){ + value = value.replace(/-/g, "/"); + value.match(/^(\d{2})\/(\d{2})\/(\d{4})$/); + if (!RegExp.$3 || RegExp.$3.length < 4) + return false; + + //@todo this is a dutch date, localization... + var dt = new Date(RegExp.$2 + "/" + RegExp.$1 + "/" + RegExp.$3); + if (dt.getFullYear() != parseFloat(RegExp.$3)) + return false; + if (dt.getMonth() != parseFloat(RegExp.$2) - 1) + return false; + if (dt.getDate() != parseFloat(RegExp.$1)) + return false; + + return true; + }, + "gYearMonth": function(value){ + value = value.replace(/-/g, "/"); + value.match(/^\/?(\d{4})(?:\d\d)?\/(\d{2})(?:\w|[\+\-]\d{2}:\d{2})?$/); + if (!RegExp.$1 || RegExp.$1.length < 4) + return false; + + var dt = new Date(value); + if (dt.getFullYear() != parseFloat(RegExp.$)) + return false; + if (dt.getMonth() != parseFloat(RegExp.$2) - 1) + return false; + + return true; + }, + "gYear": function(value){ + value.match(/^\/?(\d{4})(?:\d\d)?(?:\w|[\+\-]\d{2}:\d{2})?$/); + if (!RegExp.$1 || RegExp.$1.length < 4) + return false; + + var dt = new Date(value); + if (dt.getFullYear() != parseFloat(RegExp.$1)) + return false; + + return true; + }, + "gMonthDay": function(value){ + value = value.replace(/-/g, "/"); + value.match(/^\/\/(\d{2})\/(\d{2})(?:\w|[\+\-]\d{2}:\d{2})?$/); + + var dt = new Date(value); + if (dt.getMonth() != parseFloat(RegExp.$1) - 1) + return false; + if (dt.getDate() != parseFloat(RegExp.$2)) + return false; + + return true; + }, + "gDay": function(value){ + value = value.replace(/-/g, "/"); + value.match(/^\/{3}(\d{2})(?:\w|[\+\-]\d{2}:\d{2})?$/); + + var dt = new Date(value); + if (dt.getDate() != parseFloat(RegExp.$1)) + return false; + + return true; + }, + "gMonth": function(value){ + value = value.replace(/-/g, "/"); + value.match(/^\/{2}(\d{2})(?:\w|[\+\-]\d{2}:\d{2})?$/); + + var dt = new Date(value); + if (dt.getMonth() != parseFloat(RegExp.$1) - 1) + return false; + + return true; + }, + + //XSD datetypes + "string": function(value){ + return typeof value == "string"; + }, + "boolean": function(value){ + return /^(true|false)$/i.test(value); + }, + "base64Binary": function(value){ + return true; + }, + "hexBinary": function(value){ + return /^(?:0x|x|#)?[A-F0-9]{0,8}$/i.test(value); + }, + "float": function(value){ + return parseFloat(value) == value; + }, + "decimal": function(value){ + return /^[0-9\.\-,]+$/.test(value); + }, + "double": function(value){ + return parseFloat(value) == value; + }, + "anyURI": function(value){ + return /^(?:\w+:\/\/)?(?:(?:[\w\-]+\.)+(?:[a-z]+)|(?:(?:1?\d?\d?|2[0-4]9|25[0-5])\.){3}(?:1?\d\d|2[0-4]9|25[0-5]))(?:\:\d+)?(?:\/([^\s\\\%]+|%[\da-f]{2})*)?$/i + .test(value); + }, + "QName": function(value){ + return true; + }, + "normalizedString": function(value){ + return true; + }, + "token": function(value){ + return true; + }, + "language": function(value){ + return true; + }, + "Name": function(value){ + return true; + }, + "NCName": function(value){ + return true; + }, + "ID": function(value){ + return true; + }, + "IDREF": function(value){ + return true; + }, + "IDREFS": function(value){ + return true; + }, + "NMTOKEN": function(value){ + return true; + }, + "NMTOKENS": function(value){ + return true; + }, + "integer": function(value){ + return parseInt(value) == value; + }, + "nonPositiveInteger": function(value){ + return parseInt(value) == value && value <= 0; + }, + "negativeInteger": function(value){ + return parseInt(value) == value && value < 0; + }, + "long": function(value){ + return parseInt(value) == value && value >= -2147483648 + && value <= 2147483647; + }, + "int": function(value){ + return parseInt(value) == value; + }, + "short": function(value){ + return parseInt(value) == value && value >= -32768 && value <= 32767; + }, + "byte": function(value){ + return parseInt(value) == value && value >= -128 && value <= 127; + }, + "nonNegativeInteger": function(value){ + return parseInt(value) == value && value >= 0; + }, + "unsignedLong": function(value){ + return parseInt(value) == value && value >= 0 && value <= 4294967295; + }, + "unsignedInt": function(value){ + return parseInt(value) == value && value >= 0; + }, + "unsignedShort": function(value){ + return parseInt(value) == value && value >= 0 && value <= 65535; + }, + "unsignedByte": function(value){ + return parseInt(value) == value && value >= 0 && value <= 255; + }, + "positiveInteger": function(value){ + return parseInt(value) == value && value > 0; + } + }, + + + + "http://ajax.org/2005/aml" : { + //Ajax.org Platform datatypes + "url": function(value){ + //@todo please make this better + return /\b(https?|ftp):\/\/([\-A-Z0-9.]+)(\/[\-A-Z0-9+&@#\/%=~_|!:,.;]*)?(\?[\-A-Z0-9+&@#\/%=~_|!:,.;]*)?/i.test(value.trim()); + }, + "website": function(value){ + //@todo please make this better + return /^(?:http:\/\/)?([\w-]+\.)+\w{2,4}$/.test(value.trim()); + }, + "email": function(value){ + // @see http://fightingforalostcause.net/misc/2006/compare-email-regex.php + return /^([\w\!\#$\%\&\'\*\+\-\/\=\?\^\`{\|\}\~]+\.)*[\w\!\#$\%\&\'\*\+\-\/\=\?\^\`{\|\}\~]+@((((([a-z0-9]{1}[a-z0-9\-]{0,62}[a-z0-9]{1})|[a-z])\.)+[a-z]{2,6})|(\d{1,3}\.){3}\d{1,3}(\:\d{1,5})?)$/i + .test(value.trim()); + }, + "creditcard": function(value){ + value = value.replace(/ /g, ""); + value = value.pad(21, "0", apf.PAD_LEFT); + for (var total = 0, r, i = value.length; i >= 0; i--) { + r = value.substr(i, 1) * (i % 2 + 1); + total += r > 9 ? r - 9 : r; + } + return total % 10 === 0; + }, + "expdate": function(value){ + value = value.replace(/-/g, "/"); + value = value.split("/");//.match(/(\d{2})\/(\d{2})/); + var dt = new Date(value[0] + "/01/" + value[1]); + //if(fulldate && dt.getFullYear() != parseFloat(value[1])) return false; + if (dt.getYear() != parseFloat(value[1])) + return false;//!fulldate && + if (dt.getMonth() != parseFloat(value[0]) - 1) + return false; + + return true; + }, + "wechars": function(value){ + return /^[0-9A-Za-z\xC0-\xCF\xD1-\xD6\xD8-\xDD\xDF-\xF6\xF8-\xFF -\.',]+$/ + .test(value) + }, + "phonenumber": function(value){ + return /^[\d\+\- \(\)]+$/.test(value) + }, + "faxnumber": function(value){ + return /^[\d\+\- \(\)]+$/.test(value) + }, + "mobile": function(value){ + return /^[\d\+\- \(\)]+$/.test(value) + } + } +}; + +apf.xsd.custumTypeHandlers = {}; + +apf.xsd.matchType = function(value, type){ + var split = type.split(":"), + prefix = split[0], + doc = apf.document, + ns = doc.$prefixes[prefix]; + type = split[1]; + if (prefix == "xsd") + ns = "http://www.w3.org/2001/XMLSchema"; + if (!ns) + ns = doc.namespaceURI || apf.ns.xhtml; + + var c = this.typeHandlers[ns]; + + //check if type is type + if (c && c[type]) + return c[type](value); + + throw new Error(apf.formatErrorString(0, null, + "Validating XSD Type", "Could not find type: " + type)); + + return true; +}; + +apf.xsd.checkType = function(type, xmlNode){ + var value = typeof xmlNode == "object" + ? apf.queryValue(xmlNode) + : xmlNode; + + if (type.indexOf(":") > -1) { + var split = type.split(":"), + prefix = split[0], + name = split[1], + doc = apf.document, + ns = doc.$prefixes[prefix]; + if (prefix == "xsd") + ns = "http://www.w3.org/2001/XMLSchema"; + if (!ns) + ns = doc.namespaceURI || apf.ns.xhtml; + + var c = this.typeHandlers[ns]; + if (c && c[name]) + return c[name](value); + } + + if (this.custumTypeHandlers[type]) { + return this.custumTypeHandlers[type](value); + } + else { + //@todo MIKE: to be implemented? + } +}; + + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/core/markup/xsd/element.js)SIZE(1869)TIME(Fri, 14 Oct 2011 08:01:44 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/core/markup/xsd/enumeration.js)SIZE(1844)TIME(Fri, 14 Oct 2011 08:01:44 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/core/markup/xsd/fractiondigits.js)SIZE(1620)TIME(Fri, 14 Oct 2011 08:01:44 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/core/markup/xsd/length.js)SIZE(1527)TIME(Fri, 14 Oct 2011 08:01:44 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/core/markup/xsd/list.js)SIZE(1215)TIME(Fri, 14 Oct 2011 08:01:44 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/core/markup/xsd/maxexclusive.js)SIZE(1553)TIME(Fri, 14 Oct 2011 08:01:44 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/core/markup/xsd/maxinclusive.js)SIZE(1568)TIME(Fri, 14 Oct 2011 08:01:44 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/core/markup/xsd/maxlength.js)SIZE(1597)TIME(Fri, 14 Oct 2011 08:01:44 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/core/markup/xsd/maxscale.js)SIZE(1436)TIME(Fri, 14 Oct 2011 08:01:44 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/core/markup/xsd/minexclusive.js)SIZE(1556)TIME(Fri, 14 Oct 2011 08:01:44 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/core/markup/xsd/mininclusive.js)SIZE(1567)TIME(Fri, 14 Oct 2011 08:01:44 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/core/markup/xsd/minlength.js)SIZE(1610)TIME(Fri, 14 Oct 2011 08:01:44 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/core/markup/xsd/minscale.js)SIZE(1436)TIME(Fri, 14 Oct 2011 08:01:44 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/core/markup/xsd/pattern.js)SIZE(1537)TIME(Fri, 14 Oct 2011 08:01:44 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/core/markup/xsd/restriction.js)SIZE(1644)TIME(Fri, 14 Oct 2011 08:01:44 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/core/markup/xsd/schema.js)SIZE(1124)TIME(Fri, 14 Oct 2011 08:01:44 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/core/markup/xsd/simpletype.js)SIZE(2201)TIME(Fri, 14 Oct 2011 08:01:44 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/core/markup/xsd/totaldigits.js)SIZE(1564)TIME(Fri, 14 Oct 2011 08:01:44 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/core/markup/xsd/union.js)SIZE(2331)TIME(Fri, 14 Oct 2011 08:01:44 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/core/markup/html5.js)SIZE(3232)TIME(Fri, 14 Oct 2011 08:01:44 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + + +/** + * Object creating the HTML5 namespace for the aml parser. + * + * @constructor + * @parser + * + * @author Ruben Daniels (ruben AT ajax DOT org) + * @version %I%, %G% + * @since 0.8 + */ +apf.html5 = new apf.AmlNamespace(); +apf.setNamespace("", apf.html5); + + + +/** + * @define input + * Remarks: + * Ajax.org Platform supports the input types specified by the WHATWG html5 spec. + * @attribute {String} type the type of input element. + * Possible values: + * email provides a way to enter an email address. + * url provides a way to enter a url. + * password provides a way to enter a password. + * datetime provides a way to pick a date and time. + * date provides a way to pick a date. + * month provides a way to pick a month. + * week provides a way to pick a week. + * time provides a way to pick a time. + * number provides a way to pick a number. + * range provides a way to select a point in a range. + * checkbox provides a way to set a boolean value. + * radio used in a set, it provides a way to select a single value from multiple options. + * file provides a way to upload a file. + * submit provides a way to submit data. + * image provides a way to submit data displaying an image instead of a button. + * reset provides a way to reset entered data. + * @addnode elements + */ +/** + * @private + */ +apf.HTML5INPUT = { + "email" : "textbox", + "url" : "textbox", + "password" : "textbox", + "datetime" : "spinner", //@todo + "date" : "calendar", + "month" : "spinner", //@todo + "week" : "spinner", //@todo + "time" : "spinner", //@todo + "number" : "spinner", + "range" : "slider", + "checkbox" : "checkbox", + "radio" : "radiobutton", + "file" : "upload", + "submit" : "submit", + "image" : "submit", + "reset" : "button" +}; + +/* way to implement this: +var o = (new function(){ +}) +o.constructor.prototype = new apf.list(); + +*/ + + + +/* #-ifdef __WITH_HTML5 +if (tagName == "input") { + objName = apf.HTML5INPUT[objName = x.getAttribute("type")] + || objName || "textbox"; +} +//#-endif*/ + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/core/markup/xforms.js)SIZE(4191)TIME(Fri, 14 Oct 2011 08:01:44 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + + + + +//XForms + + + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/core/markup/xinclude.js)SIZE(1325)TIME(Fri, 14 Oct 2011 08:01:44 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + + +/** + * Object creating the XML Include namespace for the aml parser. + * + * @constructor + * @parser + * + * @allownode simpleType, complexType + * + * @author Ruben Daniels (ruben AT ajax DOT org) + * @version %I%, %G% + * @since 0.8 + */ +apf.xinclude = new apf.AmlNamespace(); +apf.setNamespace("http://www.w3.org/2001/XInclude", apf.xinclude); + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/core/markup/xinclude/fallback.js)SIZE(1322)TIME(Fri, 14 Oct 2011 08:01:44 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/core/markup/xinclude/include.js)SIZE(7025)TIME(Fri, 14 Oct 2011 08:01:44 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + +/** + * Defines a list of acceptable values + */ +apf.XiInclude = function(struct, tagName){ + this.$init(tagName || "include", apf.NODE_HIDDEN, struct); +}; + +apf.xinclude.setElement("include", apf.XiInclude); +apf.aml.setElement("include", apf.XiInclude); + +//@todo test defer="true" situation +(function(){ + this.$parsePrio = "002"; + + //1 = force no bind rule, 2 = force bind rule + /*this.$attrExcludePropBind = apf.extend({ + href : 1, + src : 1 + }, this.$attrExcludePropBind);*/ + + this.$propHandlers["href"] = + this.$propHandlers["src"] = function(value){ + if (typeof value != "string") + return finish.call(this, value); + + if (value.trim().charAt(0) == "<") { + loadIncludeFile.call(this, value.trim()); + return; + } + + this.$path = value.charAt(0) == "{" //@todo this shouldn't happen anymore + ? value + : apf.getAbsolutePath(apf.hostPath, value); + + var domParser = this.ownerDocument.$domParser; + if (!this.defer) { + domParser.$shouldWait++; + this.$parseContext = domParser.$parseContext || [this.parentNode]; + } + + //var basePath = apf.hostPath;//only for recursion: apf.getDirname(xmlNode.getAttribute("filename")) || + loadIncludeFile.call(this, this.$path); + }; + + function done(xmlNode) { + if (this.callback) { + this.callback({ + xmlNode : xmlNode, + amlNode : this.parentNode, + addedNode: this.previousSibling || this.nextSibling + }) + } + + //@todo hack!! this should never happen. Find out why it happens + if (this.parentNode) + this.parentNode.removeChild(this); + } + + function finish(xmlNode){ + var domParser = this.ownerDocument.$domParser; + + if (this.clear) + this.parentNode.$int.innerHTML = ""; + + if (xmlNode) { + domParser.parseFromXml(xmlNode, { + doc : this.ownerDocument, + amlNode : this.parentNode, + beforeNode : this, + include : true + }); + + if (!this.defer && this.$parseContext) { + var o = (this.$parseContext[1] || (this.$parseContext[1] = {})), + cb = o.callback, + _self = this; + + o.callback = function(){ + done.call(_self, xmlNode); + if (cb) + cb.call(_self.ownerDocument); + }; + + //@todo this is wrong... probably based on load order of last include element. Please rearchitect parse continuation. + if (domParser.$continueParsing.apply(domParser, this.$parseContext) === false) { + var o2 = (domParser.$parseContext[1] || (domParser.$parseContext[1] = {})), + cb2 = o.callback; + o2.callback = function(){ + if (cb) + cb.call(_self.ownerDocument); + domParser.$continueParsing.apply(domParser, _self.$parseContext); + }; + } + } + else + done.call(this, xmlNode); + } + else { + if (!this.defer) + domParser.$continueParsing.apply(domParser, this.$parseContext); + + done.call(this, xmlNode); + } + } + + function loadIncludeFile(path){ + + + var _self = this; + apf.getData(path, apf.extend(this.options || {}, { + + callback : function(xmlString, state, extra){ + if (state != apf.SUCCESS) { + var oError = new Error(apf.formatErrorString(1007, + _self, "Loading Includes", "Could not load Include file '" + + (path || _self.src) + + "'\nReason: " + extra.message)); + + if (extra.tpModule.retryTimeout(extra, state, null, oError) === true) + return true; + + apf.console.error(oError.message); + + finish.call(_self, null); + + //throw oError; + return; + } + + //@todo apf3.0 please make one way of doing this + xmlString = xmlString.replace(/\<\!DOCTYPE[^>]*>/, "") + .replace(/^[\r\n\s]*/, ""); //.replace(/ /g, " ") + if (!apf.supportNamespaces) + xmlString = xmlString.replace(/xmlns\=\"[^"]*\"/g, ""); + + if (xmlString.indexOf("" + + xmlString + ""; + + var xmlNode = apf.getXml(xmlString, null, true);//apf.getAmlDocFromString(xmlString); + + if (!xmlNode) { + throw new Error(apf.formatErrorString(0, null, + "Loading include", + "Could not parse include file. Maybe the file does not exist?", xmlNode)); + } + xmlNode.setAttribute("filename", extra.url); + + + + finish.call(_self, xmlNode); //@todo add recursive includes support here + }, + async : true, + ignoreOffline : true + })); + } +}).call(apf.XiInclude.prototype = new apf.AmlElement()); + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/core/markup/xslt/xslt.js)SIZE(13722)TIME(Fri, 14 Oct 2011 08:01:44 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/core/baseclasses/liveedit.js)SIZE(34637)TIME(Fri, 14 Oct 2011 08:01:44 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ +apf.__LIVEEDIT__ = 1 << 23; + + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/core/baseclasses/liveedit/richtext.js)SIZE(53610)TIME(Fri, 14 Oct 2011 08:01:44 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/core/baseclasses/liveedit/anchor.js)SIZE(4565)TIME(Fri, 14 Oct 2011 08:01:44 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/core/baseclasses/liveedit/blockquote.js)SIZE(1594)TIME(Fri, 14 Oct 2011 08:01:44 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/core/baseclasses/liveedit/charmap.js)SIZE(6951)TIME(Fri, 14 Oct 2011 08:01:44 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/core/baseclasses/liveedit/clipboard.js)SIZE(13429)TIME(Fri, 14 Oct 2011 08:01:44 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/core/baseclasses/liveedit/code.js)SIZE(11899)TIME(Fri, 14 Oct 2011 08:01:44 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/core/baseclasses/liveedit/color.js)SIZE(7167)TIME(Fri, 14 Oct 2011 08:01:44 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/core/baseclasses/liveedit/datetime.js)SIZE(3585)TIME(Fri, 14 Oct 2011 08:01:44 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/core/baseclasses/liveedit/directions.js)SIZE(1579)TIME(Fri, 14 Oct 2011 08:01:44 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/core/baseclasses/liveedit/emotions.js)SIZE(4322)TIME(Fri, 14 Oct 2011 08:01:44 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/core/baseclasses/liveedit/fontbase.js)SIZE(8575)TIME(Fri, 14 Oct 2011 08:01:44 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/core/baseclasses/liveedit/fontstyle.js)SIZE(25741)TIME(Fri, 14 Oct 2011 08:01:44 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/core/baseclasses/liveedit/help.js)SIZE(1485)TIME(Fri, 14 Oct 2011 08:01:44 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/core/baseclasses/liveedit/hr.js)SIZE(1593)TIME(Fri, 14 Oct 2011 08:01:44 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/core/baseclasses/liveedit/image.js)SIZE(5033)TIME(Fri, 14 Oct 2011 08:01:44 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/core/baseclasses/liveedit/links.js)SIZE(7721)TIME(Fri, 14 Oct 2011 08:01:44 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/core/baseclasses/liveedit/list.js)SIZE(4641)TIME(Fri, 14 Oct 2011 08:01:44 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/core/baseclasses/liveedit/media.js)SIZE(1489)TIME(Fri, 14 Oct 2011 08:01:44 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/core/baseclasses/liveedit/printing.js)SIZE(2098)TIME(Fri, 14 Oct 2011 08:01:44 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/core/baseclasses/liveedit/search.js)SIZE(10436)TIME(Fri, 14 Oct 2011 08:01:44 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/core/baseclasses/liveedit/spell.js)SIZE(11849)TIME(Fri, 14 Oct 2011 08:01:44 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/core/baseclasses/liveedit/subsup.js)SIZE(1935)TIME(Fri, 14 Oct 2011 08:01:44 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/core/baseclasses/liveedit/tables.js)SIZE(27128)TIME(Fri, 14 Oct 2011 08:01:44 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/core/baseclasses/liveedit/visualaid.js)SIZE(1736)TIME(Fri, 14 Oct 2011 08:01:44 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/core/baseclasses/anchoring.js)SIZE(18882)TIME(Tue, 13 Dec 2011 13:33:58 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + +apf.__ANCHORING__ = 1 << 13; + + + +/** + * All elements inheriting from this {@link term.baseclass baseclass} have anchoring features. Each side of the + * element can be attached at a certain distance to it's parent's rectangle. + * When the parent is resized the anchored side of the element stays + * at the specified distance at all times. If both sides are anchored the + * element size is changed to make sure the specified distance is maintained. + * Example: + * This example shows a bar that has 10% as a margin around it and contains a + * frame that is displayed using different calculations and settings. + * + * + * + * + * + * Remarks: + * This is one of three positioning methods. + * See {@link baseclass.alignment} + * See {@link element.grid} + * + * @constructor + * @baseclass + * @author Ruben Daniels (ruben AT ajax DOT org) + * @version %I%, %G% + * @since 0.3 + */ +apf.Anchoring = function(){ + this.$regbase = this.$regbase | apf.__ANCHORING__; + this.$anchors = []; + + var VERTICAL = 1; + var HORIZONTAL = 2; + + this.$updateQueue = 0; + this.$inited = + this.$parsed = + this.$anchoringEnabled = false; + this.$hordiff = + this.$verdiff = 0; + this.$rule_v = + this.$rule_h = + this.$rule_header = ""; + + var l = apf.layout; + + this.$supportedProperties.push("anchors"); + + var propHandlers = { + "right" : function(value, prop){ + if (!this.$anchoringEnabled && !this.$setLayout("anchoring")) + return; + + if (!value && value !== 0) + this.$ext.style[prop] = ""; + + //@note Removed apf.isParsing here to activate general queuing + if (!this.$updateQueue) + l.queue(this.$pHtmlNode, this); + this.$updateQueue = this.$updateQueue | HORIZONTAL; + }, + + "bottom" : function(value, prop){ + if (!this.$anchoringEnabled && !this.$setLayout("anchoring")) + return; + + if (!value && value !== 0) + this.$ext.style[prop] = ""; + + //@note Removed apf.isParsing here to activate general queuing + if (!this.$updateQueue) + l.queue(this.$pHtmlNode, this); + this.$updateQueue = this.$updateQueue | VERTICAL; + } + }; + propHandlers.left = propHandlers.width = propHandlers.right; + propHandlers.top = propHandlers.height = propHandlers.bottom; + + this.$propHandlers["anchors"] = function(value){ + this.$anchors = value ? value.splitSafe("(?:, *| )") : []; + + if (!this.$anchoringEnabled && !this.$setLayout("anchoring")) + return; + + if (!this.$updateQueue && apf.loaded) + l.queue(this.$pHtmlNode, this); + this.$updateQueue = this.$updateQueue | HORIZONTAL | VERTICAL; + }; + + /** + * Turns anchoring off. + * + */ + this.$disableAnchoring = function(activate){ + //!this.$parsed || + if (!this.$inited || !this.$anchoringEnabled || !this.$pHtmlNode) + return; + + l.removeRule(this.$pHtmlNode, this.$uniqueId + "_anchors"); + if (l.queue) + l.queue(this.$pHtmlNode); + + for (var prop in propHandlers) { + delete this.$propHandlers[prop]; + } + + this.removeEventListener("DOMNodeRemoved", remove); + this.removeEventListener("DOMNodeInserted", reparent); + + if (this.$ext) { + this.$ext.style.left = + this.$ext.style.right = + this.$ext.style.top = + this.$ext.style.bottom = + this.$ext.style.width = + this.$ext.style.height = + this.$ext.style.position = ""; + } + + /*if (this.right) + this.$ext.style.left = apf.getHtmlLeft(this.$ext) + "px"; + + if (this.bottom) + this.$ext.style.top = apf.getHtmlTop(this.$ext) + "px";*/ + + this.removeEventListener("prop.visible", visibleHandler); + + this.$inited = false; + this.$anchoringEnabled = false; //isn't this redundant? + }; + + /** + * Enables anchoring based on attributes set in the AML of this element + */ + /* + * @attribute {Number, String} [left] a way to determine the amount of pixels from the left border of this element to the left edge of it's parent's border. This attribute can also contain percentages, arithmetic and even full expressions. + * Example: + * + * @attribute {Number, String} [right] a way to determine the amount of pixels from the right border of this element to the right edge of it's parent's border.This attribute can also contain percentages, arithmetic and even full expressions. + * Example: + * + * @attribute {Number, String} [width] a way to determine the amount of pixels from the left border to the right border of this element.This attribute can also contain percentages, arithmetic and even full expressions. + * Example: + * + * @attribute {Number, String} [top] a way to determine the amount of pixels from the top border of this element to the top edge of it's parent's border.This attribute can also contain percentages, arithmetic and even full expressions. + * Example: + * + * @attribute {Number, String} [bottom] a way to determine the amount of pixels from the bottom border of this element to the bottom edge of it's parent's border.This attribute can also contain percentages, arithmetic and even full expressions. + * Example: + * + * @attribute {Number, String} [height] a way to determine the amount of pixels from the top border to the bottom border of this element.This attribute can also contain percentages, arithmetic and even full expressions. + * Example: + * + */ + this.$enableAnchoring = function(){ + if (this.$inited) //@todo add code to reenable anchoring rules (when showing) + return; + + /**** Properties and Attributes ****/ + apf.extend(this.$propHandlers, propHandlers); + + /**** Event handlers ****/ + this.addEventListener("DOMNodeRemoved", remove); + this.addEventListener("DOMNodeInserted", reparent); + this.addEventListener("prop.visible", visibleHandler); + + this.$updateQueue = 0 + | ((this.left || this.width || this.right || this.anchors) && HORIZONTAL) + | ((this.top || this.height || this.bottom || this.anchors) && VERTICAL) ; + + if (this.$updateQueue) + l.queue(this.$pHtmlNode, this); + + this.$inited = true; + this.$anchoringEnabled = true; + }; + + function visibleHandler(e){ + if (!(this.$rule_header || this.$rule_v || this.$rule_h) || !this.parentNode) + return; + + if (e.value) { + if (this.$rule_v || this.$rule_h) { + var rules = this.$rule_header + "\n" + this.$rule_v + "\n" + this.$rule_h; + l.setRules(this.$pHtmlNode, this.$uniqueId + "_anchors", rules); + //this.$ext.style.display = "none"; + l.queue(this.$pHtmlNode, this); + } + l.processQueue(); + } + else { + l.removeRule(this.$pHtmlNode, this.$uniqueId + "_anchors"); + l.queue(this.$pHtmlNode) + } + } + + function remove(e){ + if (e && (e.$doOnlyAdmin || e.currentTarget != this)) + return; + + if (l.queue && this.$pHtmlNode) { + l.removeRule(this.$pHtmlNode, this.$uniqueId + "_anchors"); + l.queue(this.$pHtmlNode) + } + } + + function reparent(e){ + if (!this.$amlLoaded || e.currentTarget != this) + return; + + if (!e.$isMoveWithinParent && this.$parsed) //@todo hmm weird state check + this.$moveAnchoringRules(e.$oldParentHtmlNode); + //else if (e.relatedNode == this) //@todo test this + //e.currentTarget.$setLayout("anchoring"); + } + + this.$moveAnchoringRules = function(oldParent, updateNow){ + var rules = oldParent && l.removeRule(oldParent, this.$uniqueId + "_anchors"); + if (rules) + l.queue(oldParent); + + if (!this.$rule_v && !this.$rule_h && !this.$rule_header) + return; + + this.$rule_header = getRuleHeader.call(this); + rules = this.$rule_header + "\n" + this.$rule_v + "\n" + this.$rule_h; + + //@todo sometimes the content is not displayed anymore (when reparenting by xinclude) + //this.$ext.style.display = "none"; + + l.setRules(this.$pHtmlNode, this.$uniqueId + "_anchors", rules); + l.queue(this.$pHtmlNode, this); + }; + + this.$hasAnchorRules = function(){ + return this.$rule_v || this.$rule_h ? true : false; + }; + + function getRuleHeader(){ + if (!this.$pHtmlDoc) return ""; + return "try{\ + var oHtml = " + (apf.hasHtmlIdsInJs + ? this.$ext.getAttribute("id") + : "document.getElementById('" + + this.$ext.getAttribute("id") + "')") + ";\ + \ + var pWidth = " + (this.$pHtmlNode == this.$pHtmlDoc.body + ? "apf.getWindowWidth()" //@todo only needed for debug? + : "apf.getHtmlInnerWidth(oHtml.parentNode)") + ";\ + \ + var pHeight = " + (this.$pHtmlNode == this.$pHtmlDoc.body + ? "apf.getWindowHeight()" //@todo only needed for debug? + : "apf.getHtmlInnerHeight(oHtml.parentNode)") + ";\ + }catch(e){\ + }"; + } + + /** + * @macro + */ + function setPercentage(expr, value){ + return String(expr).replace(apf.percentageMatch, "((" + value + " * $1)/100)"); + } + + + this.$recalcAnchoring = function(queueDelay){ + this.$updateQueue = this.$updateQueue | HORIZONTAL | VERTICAL; + this.$updateLayout(); + l.queue(this.$pHtmlNode, this); + + if (!queueDelay) + l.processQueue(); + }; + + + function visCheck(){ + if (this.$updateQueue) { + this.$updateLayout(); + apf.layout.activateRules(this.$ext.parentNode); + } + } + + this.$updateLayout = function(){ + if (!this.$anchoringEnabled) + return; + + if (!apf.window.vManager.check(this, "anchoring", visCheck)) + return; + + if (!this.$parsed) { + if (!this.$ext.getAttribute("id")) + apf.setUniqueHtmlId(this.$ext); + + this.$rule_header = getRuleHeader.call(this); + this.$parsed = true; + } + + if (!this.$updateQueue) { + if (this.visible && this.$ext.style.display == "none") + this.$ext.style.display = ""; + return; + } + + if (this.draggable == "relative") { + if ("absolute|fixed|relative".indexOf(apf.getStyle(this.$ext, "position")) == -1) //@todo apf3.1 the IDE doesn't like this + this.$ext.style.position = "absolute"; + } + else if (this.left || this.left === 0 || this.top || this.top === 0 + || this.right || this.right === 0 || this.bottom || this.bottom === 0 + || this.$anchors.length) { + if ("absolute|fixed".indexOf(apf.getStyle(this.$ext, "position")) == -1) + this.$ext.style.position = "absolute"; + } + else if (!this.center) { + if ("absolute|fixed|relative".indexOf(apf.getStyle(this.$ext, "position")) == -1) + this.$ext.style.position = "relative"; + if (!this.width) + this.$ext.style.width = ""; + if (!this.height) + this.$ext.style.height = ""; + } + + var rules; + if (this.$updateQueue & HORIZONTAL) { + rules = []; + + this.$hordiff = apf.getWidthDiff(this.$ext); + + var left = this.$anchors[3] || this.left, + right = this.$anchors[1] || this.right, + width = this.width, hasLeft = left || left === 0, + hasRight = right || right === 0, + hasWidth = width || width === 0; + + if (right && typeof right == "string") + right = setPercentage(right, "pWidth"); + + if (hasLeft) { + if (parseInt(left) != left) { + left = setPercentage(left, "pWidth"); + rules.push("oHtml.style.left = (" + left + ") + 'px'"); + } + else + this.$ext.style.left = left + "px"; + } + if ((apf.hasStyleAnchors || !hasLeft) && hasRight) { + if (parseInt(right) != right) { + right = setPercentage(right, "pWidth"); + rules.push("oHtml.style.right = (" + right + ") + 'px'"); + } + else + this.$ext.style.right = right + "px"; + } + + if (hasLeft && hasRight) { //right != null && left != null) { + if (!apf.hasStyleAnchors) + rules.push("oHtml.style.width = (pWidth - (" + right + + ") - (" + left + ") - " + this.$hordiff + ") + 'px'"); + else + this.$ext.style.width = ""; + } + else if (hasWidth && typeof this.maxwidth == "number" && typeof this.minwidth == "number") { + if (parseInt(width) != width) { + width = setPercentage(width, "pWidth"); + rules.push("oHtml.style.width = Math.max(" + + (this.minwidth - this.$hordiff) + + ", Math.min(" + (this.maxwidth - this.$hordiff) + ", " + + width + " - " + this.$hordiff + ")) + 'px'"); + } + else { + this.$ext.style.width = ((width > this.minwidth + ? (width < this.maxwidth + ? width + : this.maxwidth) + : this.minwidth) - this.$hordiff) + "px"; + } + } + + this.$rule_h = (rules.length + ? "try{" + rules.join(";}catch(e){};try{") + ";}catch(e){};" + : ""); + } + + if (this.$updateQueue & VERTICAL) { + rules = []; + + this.$verdiff = apf.getHeightDiff(this.$ext); + + var top = this.$anchors[0] || this.top, + bottom = this.$anchors[2] || this.bottom, + height = this.height, hasTop = top || top === 0, + hasBottom = bottom || bottom === 0, + hasHeight = height || height === 0; + + if (bottom && typeof bottom == "string") + bottom = setPercentage(bottom, "pHeight"); + + if (hasTop) { + if (parseInt(top) != top) { + top = setPercentage(top, "pHeight"); + rules.push("oHtml.style.top = (" + top + ") + 'px'"); + } + else + this.$ext.style.top = top + "px"; + } + if ((apf.hasStyleAnchors || !hasTop) && hasBottom) { + if (parseInt(bottom) != bottom) { + rules.push("oHtml.style.bottom = (" + bottom + ") + 'px'"); + } + else + this.$ext.style.bottom = bottom + "px"; + } + if (hasTop && hasBottom) { //bottom != null && top != null) { + if (!apf.hasStyleAnchors) + rules.push("oHtml.style.height = (pHeight - (" + bottom + + ") - (" + top + ") - " + this.$verdiff + ") + 'px'"); + else + this.$ext.style.height = ""; + } + else if (hasHeight && typeof this.minheight == "number") { + if (parseInt(height) != height) { + height = setPercentage(height, "pHeight"); + rules.push("oHtml.style.height = Math.max(" + + (this.minheight - this.$verdiff) + + ", Math.min(" + (this.maxheight - this.$verdiff) + ", " + + height + " - " + this.$verdiff + ")) + 'px'"); + } + else { + this.$ext.style.height = Math.max(0, (height > this.minheight + ? (height < this.maxheight + ? height + : this.maxheight) + : this.minheight) - this.$verdiff) + "px"; + } + } + + this.$rule_v = (rules.length + ? "try{" + rules.join(";}catch(e){};try{") + ";}catch(e){};" + : ""); + } + + if (this.$rule_v || this.$rule_h) { + l.setRules(this.$pHtmlNode, this.$uniqueId + "_anchors", + this.$rule_header + "\n" + this.$rule_v + "\n" + this.$rule_h, true); + } + else { + l.removeRule(this.$pHtmlNode, this.$uniqueId + "_anchors"); + } + + this.$updateQueue = 0; + + if (this.$box && !apf.hasFlexibleBox) //temporary fix + apf.layout.forceResize(this.$ext); + }; + + this.addEventListener("DOMNodeInsertedIntoDocument", function(e){ + //if (this.$updateQueue) + //this.$updateLayout(); + }); + + this.addEventListener("DOMNodeRemovedFromDocument", function(e){ + this.$disableAnchoring(); + }); +}; + + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/core/baseclasses/contenteditable.js)SIZE(20162)TIME(Fri, 14 Oct 2011 08:01:44 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ +apf.__CONTENTEDITABLE__ = 1 << 24; + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/core/baseclasses/guielement.js)SIZE(33095)TIME(Wed, 30 Nov 2011 17:29:20 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + +apf.__GUIELEMENT__ = 1 << 15; +apf.__VALIDATION__ = 1 << 6; + + + +/** + * All elements inheriting from this {@link term.baseclass baseclass} are a aml component. + * + * @constructor + * @baseclass + * @author Ruben Daniels (ruben AT ajax DOT org) + * @version %I%, %G% + * @since 0.4 + * + * @inherits apf.Anchoring + * @inherits apf.DelayedRender + * @inherits apf.DragDrop + * @inherits apf.Focussable + * @inherits apf.Interactive + * @inherits apf.Transaction + * @inherits apf.Validation + * + * @attribute {String} span the number of columns this element spans. Only used inside a table element. + * @attribute {String} margin + * @todo attribute align + * + * @attribute {mixed} left the left position of this element. Depending + * on the choosen layout method the unit can be pixels, a percentage or an + * expression. + * + * @attribute {mixed} top the top position of this element. Depending + * on the choosen layout method the unit can be pixels, a percentage or an + * expression. + * + * @attribute {mixed} right the right position of this element. Depending + * on the choosen layout method the unit can be pixels, a percentage or an + * expression. + * + * @attribute {mixed} bottom the bottom position of this element. Depending + * on the choosen layout method the unit can be pixels, a percentage or an + * expression. + * + * @attribute {mixed} width the different between the left edge and the + * right edge of this element. Depending on the choosen layout method the + * unit can be pixels, a percentage or an expression. + * Remarks: + * When used as a child of a grid element the width can also be set as '*'. + * This will fill the rest space. + * + * @attribute {mixed} height the different between the top edge and the + * bottom edge of this element. Depending on the choosen layout method the + * unit can be pixels, a percentage or an expression. + * Remarks: + * When used as a child of a grid element the height can also be set as '*'. + * This will fill the rest space. + * + * @event resize Fires when the element changes width or height. + * + * @event contextmenu Fires when the user requests a context menu. Either + * using the keyboard or mouse. + * bubbles: yes + * cancelable: Prevents the default contextmenu from appearing. + * object: + * {Number} x the x coordinate where the contextmenu is requested on. + * {Number} y the y coordinate where the contextmenu is requested on. + * {Event} htmlEvent the html event object that triggered this event from being called. + * @event focus Fires when this element receives focus. + * @event blur Fires when this element loses focus. + * @event keydown Fires when this element has focus and the user presses a key on the keyboard. + * cancelable: Prevents the default key action. + * bubbles: + * object: + * {Boolean} ctrlKey whether the ctrl key was pressed. + * {Boolean} shiftKey whether the shift key was pressed. + * {Boolean} altKey whether the alt key was pressed. + * {Number} keyCode which key was pressed. This is an ascii number. + * {Event} htmlEvent the html event object that triggered this event from being called. + */ +apf.GuiElement = function(){ + this.$init(true); +}; + +(function(){ + this.$regbase = this.$regbase | apf.__GUIELEMENT__; + + this.$focussable = apf.KEYBOARD_MOUSE; // Each GUINODE can get the focus by default + this.visible = 2; //default value; + + /*this.minwidth = 5; + this.minheight = 5; + this.maxwidth = 10000; + this.maxheight = 10000;*/ + + + this.$booleanProperties["disable-keyboard"] = true; + + this.$booleanProperties["visible"] = true; + this.$booleanProperties["focussable"] = true; + + + this.$supportedProperties.push("draggable", "resizable"); + + this.$supportedProperties.push( + "focussable", "zindex", "disabled", "tabindex", + "disable-keyboard", "contextmenu", "visible", "autosize", + "loadaml", "actiontracker", "alias", + "width", "left", "top", "height", "tooltip" + ); + + this.$setLayout = function(type, insert){ + if (!this.$drawn || !this.$pHtmlNode) + return false; + + if (this.parentNode) { + + if (this.parentNode.localName == "table") { + if (this.$disableCurrentLayout) + this.$disableCurrentLayout(); + this.parentNode.register(this, insert); + this.$disableCurrentLayout = null; + return type == "table"; + }else + + + + if ("vbox|hbox".indexOf(this.parentNode.localName) > -1) { + if (this.$disableCurrentLayout) + this.$disableCurrentLayout(); + this.parentNode.register(this, insert); + this.$disableCurrentLayout = null; + return type == this.parentNode.localName; + } //else + + } + + + if (!this.$anchoringEnabled) { + if (this.$disableCurrentLayout) + this.$disableCurrentLayout(); + this.$enableAnchoring(); + this.$disableCurrentLayout = this.$disableAnchoring; + } + return type == "anchoring"; + + } + + this.addEventListener("DOMNodeInserted", function(e){ + if (e.currentTarget == this + && "vbox|hbox|table".indexOf(this.parentNode.localName) == -1) { + this.$setLayout(); + } + }); + + this.implement( + + apf.Anchoring + + + + ); + + /**** Convenience functions for gui nodes ****/ + + + + /**** Geometry ****/ + + /** + * Sets the different between the left edge and the right edge of this + * element. Depending on the choosen layout method the unit can be + * pixels, a percentage or an expression. + * Call-chaining is supported. + * @param {Number} value the new width of this element. + */ + this.setWidth = function(value){ + this.setProperty("width", value, false, true); + return this; + }; + + /** + * Sets the different between the top edge and the bottom edge of this + * element. Depending on the choosen layout method the unit can be + * pixels, a percentage or an expression. + * Call-chaining is supported. + * @param {Number} value the new height of this element. + */ + this.setHeight = function(value){ + this.setProperty("height", value, false, true); + return this; + }; + + /** + * Sets the left position of this element. Depending on the choosen + * layout method the unit can be pixels, a percentage or an expression. + * Call-chaining is supported. + * @param {Number} value the new left position of this element. + */ + this.setLeft = function(value){ + this.setProperty("left", value, false, true); + return this; + }; + + /** + * Sets the top position of this element. Depending on the choosen + * layout method the unit can be pixels, a percentage or an expression. + * Call-chaining is supported. + * @param {Number} value the new top position of this element. + */ + this.setTop = function(value){ + this.setProperty("top", value, false, true); + return this; + }; + + if (!this.show) { + /** + * Makes the elements visible. Call-chaining is supported. + */ + this.show = function(){ + this.setProperty("visible", true, false, true); + return this; + }; + } + + if (!this.hide) { + /** + * Makes the elements invisible. Call-chaining is supported. + */ + this.hide = function(){ + this.setProperty("visible", false, false, true); + return this; + }; + } + + /** + * Retrieves the calculated width in pixels for this element + */ + this.getWidth = function(){ + return (this.$ext || {}).offsetWidth; + }; + + /** + * Retrieves the calculated height in pixels for this element + */ + this.getHeight = function(){ + return (this.$ext || {}).offsetHeight; + }; + + /** + * Retrieves the calculated left position in pixels for this element + * relative to the offsetParent. + */ + this.getLeft = function(){ + return (this.$ext || {}).offsetLeft; + }; + + /** + * Retrieves the calculated top position in pixels for this element + * relative to the offsetParent. + */ + this.getTop = function(){ + return (this.$ext || {}).offsetTop; + }; + + /**** Disabling ****/ + + /** + * Activates the functions of this element. Call-chaining is supported. + */ + this.enable = function(){ + this.setProperty("disabled", false, false, true); + return this; + }; + + /** + * Deactivates the functions of this element. + * Call-chaining is supported. + */ + this.disable = function(){ + this.setProperty("disabled", true, false, true); + return this; + }; + + /**** z-Index ****/ + + /** + * Moves this element to the lowest z ordered level. + * Call-chaining is supported. + */ + this.sendToBack = function(){ + this.setProperty("zindex", 0, false, true); + return this; + }; + + /** + * Moves this element to the highest z ordered level. + * Call-chaining is supported. + */ + this.bringToFront = function(){ + this.setProperty("zindex", apf.all.length + 1, false, true); + return this; + }; + + /** + * Moves this element one z order level deeper. + * Call-chaining is supported. + */ + this.sendBackwards = function(){ + this.setProperty("zindex", this.zindex - 1, false, true); + return this; + }; + + /** + * Moves this element one z order level higher. + * Call-chaining is supported. + */ + this.bringForward = function(){ + this.setProperty("zindex", this.zindex + 1, false, true); + return this; + }; + + + + this.hasFocus = function(){} + + this.addEventListener("DOMNodeInsertedIntoDocument", function(e){ + var x = this.$aml; + + // will $pHtmlNode be deprecated soon? + // check used to be: + //if (!this.$pHtmlNode && this.parentNode) + if (this.parentNode) { + if (this.localName == "item" + && this.parentNode.hasFeature(apf.__MULTISELECT__)) //special case for item nodes, using multiselect rendering + this.$pHtmlNode = this.parentNode.$container; + else + this.$pHtmlNode = this.parentNode.$int; //@todo apf3.0 change this in the mutation events + } + + if (!this.$pHtmlNode) //@todo apf3.0 retry on DOMNodeInserted + return; + + this.$pHtmlDoc = this.$pHtmlNode.ownerDocument || document; + + if (this.$initSkin) + this.$initSkin(x); + + if (this.$draw) + this.$draw(); + + if (e.id) + this.$ext.setAttribute("id", e.id); + + if (typeof this.visible == "undefined") + this.visible = true; + + + + + if (this.$focussable && typeof this.focussable == "undefined") + apf.GuiElement.propHandlers.focussable.call(this); + + + this.$drawn = true; + }, true); + + var f = function(e){ + if (!this.$pHtmlNode) //@todo apf3.0 retry on DOMInsert or whatever its called + return; + + this.$setLayout(); //@todo apf3.0 moving an element minwidth/height should be recalced + + //@todo apf3.0 set this also for skin change + if (this.$ext) { + var hasPres = (this.hasFeature(apf.__PRESENTATION__)) || false; + var type = this.$isLeechingSkin ? this.localName : "main"; + if (this.minwidth == undefined) + this.minwidth = apf.getCoord(hasPres && parseInt(this.$getOption(type, "minwidth")), 0); + if (this.minheight == undefined) + this.minheight = apf.getCoord(hasPres && parseInt(this.$getOption(type, "minheight")), 0); + if (this.maxwidth == undefined) + this.maxwidth = apf.getCoord(hasPres && parseInt(this.$getOption(type, "maxwidth")), 10000); + if (this.maxheight == undefined) + this.maxheight = apf.getCoord(hasPres && parseInt(this.$getOption(type, "maxheight")), 10000); + + //--#ifdef __WITH_CONTENTEDITABLE + //@todo slow?? + var diff = apf.getDiff(this.$ext); + this.$ext.style.minWidth = Math.max(0, this.minwidth - diff[0]) + "px"; + this.$ext.style.minHeight = Math.max(0, this.minheight - diff[1]) + "px"; + this.$ext.style.maxWidth = Math.max(0, this.maxwidth - diff[0]) + "px"; + this.$ext.style.maxHeight = Math.max(0, this.maxheight - diff[1]) + "px"; + + if (this.$altExt && apf.isGecko) { + this.$altExt.style.minHeight = this.$ext.style.minHeight; + this.$altExt.style.maxHeight = this.$ext.style.maxHeight; + this.$altExt.style.minWidth = this.$ext.style.minWidth; + this.$altExt.style.maxWidth = this.$ext.style.maxWidth; + } + //--#endif + } + + if (this.$loadAml) + this.$loadAml(this.$aml); //@todo replace by event + }; + + this.addEventListener("DOMNodeInsertedIntoDocument", f); + this.addEventListener("$skinchange", f); + + + var f; + this.addEventListener("$event.resize", f = function(c){ + apf.layout.setRules(this.$ext, "resize", "var o = apf.all[" + this.$uniqueId + "];\ + if (o) o.dispatchEvent('resize');", true); + + apf.layout.queue(this.$ext); + //apf.layout.activateRules(this.$ext); + this.removeEventListener("$event.resize", f); + }); + + + + this.addEventListener("contextmenu", function(e){ + + + if (!this.contextmenus) return; + + if (this.hasFeature(apf.__DATABINDING__)) { + var contextmenu; + var xmlNode = this.hasFeature(apf.__MULTISELECT__) + ? this.selected + : this.xmlRoot; + + var i, l, m, isRef, sel, menuId, cm, result; + for (i = 0, l = this.contextmenus.length; i < l; i++) { + isRef = (typeof (cm = this.contextmenus[i]) == "string"); + result = null; + if (!isRef && cm.match && xmlNode) {//@todo apf3.0 cache this statement + result = (cm.cmatch || (cm.cmatch = apf.lm.compile(cm.match, { + xpathmode : 3, + injectself : true + })))(xmlNode) + } + + if (isRef || xmlNode && result || !cm.match) { //!xmlNode && + menuId = isRef + ? cm + : cm.menu + + if (!self[menuId]) { + + + return; + } + + self[menuId].display(e.x, e.y, null, this, xmlNode); + + e.returnValue = false;//htmlEvent. + e.cancelBubble = true; + break; + } + } + + //IE6 compatiblity + /* + @todo please test that disabling this is OK + if (!apf.config.disableRightClick) { + document.oncontextmenu = function(){ + document.oncontextmenu = null; + e.cancelBubble = true; + return false; + } + }*/ + } + else { + menuId = typeof this.contextmenus[0] == "string" + ? this.contextmenus[0] + : this.contextmenus[0].getAttribute("menu") + + if (!self[menuId]) { + + + return; + } + + self[menuId].display(e.x, e.y, null, this); + + e.returnValue = false;//htmlEvent. + e.cancelBubble = true; + } + }); + +}).call(apf.GuiElement.prototype = new apf.AmlElement()); + +/** + * @for apf.amlNode + * @private + */ +apf.GuiElement.propHandlers = { + + /** + * @attribute {Boolean} focussable whether this element can receive the focus. + * The focussed element receives keyboard event.s + */ + "focussable": function(value){ + this.focussable = typeof value == "undefined" || value; + + if (!this.hasFeature(apf.__FOCUSSABLE__)) //@todo should this be on the prototype + this.implement(apf.Focussable); + + if (this.focussable) { + apf.window.$addFocus(this, this.tabindex); + } + else { + apf.window.$removeFocus(this); + } + }, + + + /** + * @attribute {Number} zindex the z ordered layer in which this element is + * drawn. + */ + "zindex": function(value){ + this.$ext.style.zIndex = value; + }, + + /** + * @attribute {Boolean} visible whether this element is shown. + */ + "visible": function(value){ + if (apf.isFalse(value) || typeof value == "undefined") { + if (this.$ext) + this.$ext.style.display = "none"; + + if (apf.document.activeElement == this || this.canHaveChildren == 2 + && apf.isChildOf(this, apf.document.activeElement, false)) { + if (apf.config.allowBlur && this.hasFeature(apf.__FOCUSSABLE__)) + this.blur(); + else + apf.window.moveNext(); + } + + this.visible = false; + } + else { //if (apf.isTrue(value)) default + if (this.$ext) { + this.$ext.style.display = ""; //Some form of inheritance detection + if (!this.$ext.offsetHeight) + this.$ext.style.display = this.$display || "block"; + } + + + if (apf.layout && this.$int) //apf.hasSingleRszEvent) + apf.layout.forceResize(this.$int);//this.$int + + + this.visible = true; + } + }, + + /** + * @attribute {Boolean} disabled whether this element's functions are active. + * For elements that can contain other apf.NODE_VISIBLE elements this + * attribute applies to all it's children. + */ + "disabled": function(value){ + if (!this.$drawn) { + var _self = this; + //this.disabled = false; + + apf.queue.add("disable" + this.$uniqueId, function(e){ + _self.disabled = value; + apf.GuiElement.propHandlers.disabled.call(_self, value); + }); + return; + } + else + apf.queue.remove("disable" + this.$uniqueId); + + //For child containers we only disable its children + if (this.canHaveChildren) { + //@todo Fix focus here first.. else it will jump whilst looping + if (value != -1) + value = this.disabled = apf.isTrue(value); + + var nodes = this.childNodes; + for (var node, i = 0, l = nodes.length; i < l; i++) { + node = nodes[i]; + if (node.nodeFunc == apf.NODE_VISIBLE) { + if (value && node.disabled != -1) + node.$disabled = node.disabled || false; + node.setProperty("disabled", value ? -1 : false); + } + } + + //this.disabled = undefined; + if (this.$isWindowContainer) + return; + } + + if (value == -1 || value == false) { + //value = true; + } + else if (typeof this.$disabled == "boolean") { + if (value === null) { + value = this.$disabled; + this.$disabled = null; + } + else { + this.$disabled = value || false; + return; + } + } + + if (apf.isTrue(value) || value == -1) { + this.disabled = false; + if (apf.document.activeElement == this) { + apf.window.moveNext(true); //@todo should not include window + if (apf.document.activeElement == this) + this.$blur(); + } + + if (this.hasFeature(apf.__PRESENTATION__)) + this.$setStyleClass(this.$ext, this.$baseCSSname + "Disabled"); + + if (this.$disable) + this.$disable(); + + + + this.disabled = value; + } + else { + if (this.hasFeature(apf.__DATABINDING__) && apf.config.autoDisable + && !(!this.$bindings || this.xmlRoot)) + return false; + + this.disabled = false; + + if (apf.document.activeElement == this) + this.$focus(); + + if (this.hasFeature(apf.__PRESENTATION__)) + this.$setStyleClass(this.$ext, null, [this.$baseCSSname + "Disabled"]); + + if (this.$enable) + this.$enable(); + + + } + }, + + /** + * @attribute {Boolean} enables whether this element's functions are active. + * For elements that can contain other apf.NODE_VISIBLE elements this + * attribute applies to all it's children. + */ + "enabled" : function(value){ + this.setProperty("disabled", !value); + }, + + /** + * @attribute {Boolean} disable-keyboard whether this element receives + * keyboard input. This allows you to disable keyboard independently from + * focus handling. + */ + "disable-keyboard": function(value){ + this.disableKeyboard = apf.isTrue(value); + }, + + /** + * @attribute {String} tooltip the text displayed when a user hovers with + * the mouse over the element. + */ + "tooltip" : function(value){ + this.$ext.setAttribute("title", (value || "") + (this.hotkey ? " (" + this.hotkey + ")" : "")); + }, + + + /** + * @attribute {String} contextmenu the name of the menu element that will + * be shown when the user right clicks or uses the context menu keyboard + * shortcut. + * Example: + * + * + * test + * test2 + * + * + * + * + * + */ + "contextmenu": function(value){ + this.contextmenus = [value]; + }, + + + + /** + * @attribute {String} actiontracker the name of the actiontracker that + * is used for this element and it's children. If the actiontracker doesn't + * exist yet it is created. + * Example: + * In this example the list uses a different actiontracker than the two + * textboxes which determine their actiontracker based on the one that + * is defined on the bar. + * + * + * + * + * + * + * + * + */ + "actiontracker": function(value){ + if (!value) { + this.$at = null; + } + else if (value.localName == "actiontracker") { + this.$at = value; + } + else { + + this.$at = typeof value == "string" && self[value] + ? apf.nameserver.get("actiontracker", value) || self[value].getActionTracker() + : apf.setReference(value, + apf.nameserver.register("actiontracker", + value, new apf.actiontracker())); + + if (!this.$at.name) + this.$at.name = value; + + } + }, + + + //Load subAML + /** + * @attribute {String} aml the {@link term.datainstruction data instruction} + * that loads new aml as children of this element. + */ + "aml": function(value){ + this.replaceMarkup(value); + } + + /** + * @attribute {String} sets this aml element to be editable + * that loads new aml as children of this element. + */ + + + +}; + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/core/baseclasses/presentation.js)SIZE(20758)TIME(Wed, 30 Nov 2011 17:29:20 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + +apf.__PRESENTATION__ = 1 << 9; + + +/** + * All elements inheriting from this {@link term.baseclass baseclass} have skinning features. A skin is a description + * of how the element is rendered. In the web browser this is done using html + * elements and css. + * Remarks: + * The skin is set using the skin attribute. The skin of each element can be + * changed at run time. Other than just changing the look of an element, a skin + * change can help the user to perceive information in a different way. For + * example a list element has a default skin, but can also use the thumbnail + * skin to display thumbnails of the {@link term.datanode data nodes}. + * + * A skin for an element is always build up out of a standard set of parts. + * + * + * + * ... + * + * + * + * + * + * ... + * + * ... + * + * + * + * The alias contains a name that contains alternative names for the skin. The + * style tags contain the css. The main tag contains the html elements that are + * created when the component is created. Any other skin items are used to render + * other elements of the widget. In this reference guide you will find these + * skin items described on the pages of each widget. + * + * @constructor + * @baseclass + * @author Ruben Daniels (ruben AT ajax DOT org) + * @version %I%, %G% + * @since 0.5 + */ +apf.Presentation = function(){ + this.$init(true); +}; + +(function(){ + this.$regbase = this.$regbase | apf.__PRESENTATION__; + + /**** Properties and Attributes ****/ + + this.$supportedProperties.push("skin"); + + /** + * @attribute {string} skinset the skinset for + * this element. If none is specified the skinset attribute + * of the appsettings is used. When not defined the default skinset + * is used. + * Example: + * + * + * + */ + this.$propHandlers["skinset"] = + + /** + * @attribute {string} skin the name of the skin in the skinset that defines + * how this element is rendered. When a skin is changed the full state of the + * element is kept including it's selection, all the + * aml attributes, loaded data, focus and disabled state. + * Example: + * + * + * + * Example: + * + * lstExample.setAttribute("skin", "list"); + * + */ + + this.$propHandlers["skin"] = function(value){ + if (!this.$amlLoaded) //If we didn't load a skin yet, this will be done when we attach to a parent + return; + + if (!this.$skinTimer) { + var _self = this; + clearTimeout(this.$skinTimer); + this.$skinTimer = $setTimeout(function(){ + changeSkin.call(_self, _self.skin); + delete _self.$skinTimer; + }); + } + } + + + /** + * @attribute {String} style the css style applied to the this element. This can be a string containing one or more css rules. + */ + this.$propHandlers["style"] = function(value){ + if (!this.styleAttrIsObj && this.$amlLoaded) + this.$ext.setAttribute("style", value); + } + + /** + * @attribute {String} border turns borders on and off. Set sizes in the seq top, right, bottom, left. + */ + this.$propHandlers["border"] = function(value){ + if (!value) + this.$ext.style.borderWidth = ""; + else + this.$ext.style.borderWidth = apf.getBox(value).join("px ") + "px"; + } + + /** + * @attribute {String} margin turns margins on and off. Set sizes in the seq top, right, bottom, left. + */ + this.$propHandlers["margin"] = function(value){ + if (!value) + this.$ext.style.margin = ""; + else + this.$ext.style.margin = apf.getBox(value).join("px ") + "px"; + } + + /** + * @attribute {String} class the name of the css style class applied to the this element. + */ + this.$propHandlers["class"] = function(value){ + this.$setStyleClass(this.$ext, value, this.$lastClassValue ? [this.$lastClassValue] : null); + this.$lastClassValue = value; + } + + + this.$forceSkinChange = function(skin, skinset){ + changeSkin.call(this, skin, skinset); + } + + //@todo objects don't always have an $int anymore.. test this + function changeSkin(skin, skinset){ + clearTimeout(this.$skinTimer); + + //var skinName = (skinset || this.skinset || apf.config.skinset) + // + ":" + (skin || this.skin || this.localName); + + + //Store selection + if (this.selectable) + var valueList = this.getSelection();//valueList; + + + //Store needed state information + var oExt = this.$ext, + oInt = this.$int, + pNode = this.$ext.parentNode, + beforeNode = oExt.nextSibling, + idExt = this.$ext.getAttribute("id"), + idInt = this.$int && this.$int.getAttribute("id"), + oldBase = this.$baseCSSname; + + if (oExt.parentNode) + oExt.parentNode.removeChild(oExt); + + //@todo changing skin will leak A LOT, should call $destroy here, with some extra magic + if (this.$destroy) + this.$destroy(true); + + //Load the new skin + this.skin = skin; + this.$loadSkin(skinset ? skinset + ":" + skin : null); + + //Draw + if (this.$draw) + this.$draw(true); + + if (idExt) + this.$ext.setAttribute("id", idExt); + + if (beforeNode || pNode != this.$ext.parentNode) + pNode.insertBefore(this.$ext, beforeNode); + + //Style + + //Border + + //Margin + + //Classes + var i, l, newclasses = [], + classes = (oExt.className || "").splitSafe("\\s+"); + for (i = 0; i < classes.length; i++) { + if (classes[i] && classes[i].indexOf(oldBase) != 0) + newclasses.push(classes[i].replace(oldBase, this.$baseCSSname)); + } + apf.setStyleClass(this.$ext, newclasses.join(" ")); + + //Copy events + var en, ev = apf.skins.events; + for (i = 0, l = ev.length; i < l; i++) { + en = ev[i]; + if (typeof oExt[en] == "function" && !this.$ext[en]) + this.$ext[en] = oExt[en]; + } + + //Copy css state (dunno if this is best) + this.$ext.style.left = oExt.style.left; + this.$ext.style.top = oExt.style.top; + this.$ext.style.width = oExt.style.width; + this.$ext.style.height = oExt.style.height; + this.$ext.style.right = oExt.style.right; + this.$ext.style.bottom = oExt.style.bottom; + this.$ext.style.zIndex = oExt.style.zIndex; + this.$ext.style.position = oExt.style.position; + this.$ext.style.display = oExt.style.display; + + //Widget specific + //if (this.$loadAml) + //this.$loadAml(this.$aml); + + if (idInt) + this.$int.setAttribute("id", idInt); + + if (this.$int && this.$int != oInt) { + var node, newNode = this.$int, nodes = oInt.childNodes; + for (var i = nodes.length - 1; i >= 0; i--) { + if ((node = nodes[i]).host) { + node.host.$pHtmlNode = newNode; + if (node.host.$isLeechingSkin) + setLeechedSkin.call(node.host); + } + newNode.insertBefore(node, newNode.firstChild); + } + //this.$int.onresize = oInt.onresize; + } + + + //DragDrop + if (this.hasFeature(apf.__DRAGDROP__)) { + if (document.elementFromPointAdd) { + document.elementFromPointRemove(oExt); + document.elementFromPointAdd(this.$ext); + } + } + + + //Check disabled state + if (this.disabled) + this.$disable(); //@todo apf3.0 test + + //Check focussed state + if (this.$focussable && apf.document.activeElement == this) + this.$focus(); //@todo apf3.0 test + + //Dispatch event + this.dispatchEvent("$skinchange", { + ext : oExt, + "int": oInt + }); + + + //Reload data + if (this.hasFeature(apf.__DATABINDING__) && this.xmlRoot) + this.reload(); + else + + if (this.value) + this.$propHandlers["value"].call(this, this.value); + + + //Set Selection + if (this.hasFeature(apf.__MULTISELECT__)) { + if (this.selectable) + this.selectList(valueList, true); + } + + + //Move layout rules + if (!apf.hasSingleRszEvent) { + apf.layout.activateRules(this.$ext); + if (this.$int) + apf.layout.activateRules(this.$int); + } + +/* + if (this.hasFeature(apf.__ANCHORING__)) + this.$recalcAnchoring(); + + + + if (this.hasFeature(apf.__ALIGNMENT__)) { + if (this.aData) + this.aData.oHtml = this.$ext; + + if (this.pData) { + this.pData.oHtml = this.$ext; + this.pData.pHtml = this.$int; + + var nodes = this.pData.childNodes; + for (i = 0; i < nodes.length; i++) + nodes[i].pHtml = this.$int; //Should this be recursive?? + } + } + +*/ + + if (this.draggable && this.$propHandlers["draggable"]) //@todo move these to the event below apf3.0) + this.$propHandlers["draggable"].call(this, this.draggable); + if (this.resizable && this.$propHandlers["resizable"]) + this.$propHandlers["resizable"].call(this, this.resizable); + + + + apf.layout.forceResize(this.$ext); + + }; + + + /**** Private methods ****/ + + this.$setStyleClass = apf.setStyleClass; + + function setLeechedSkin(e){ + if (!this.$amlLoaded || e && (e.$isMoveWithinParent + || e.currentTarget != this || !e.$oldParent)) + return; + + this.$setInheritedAttribute(this, "skinset"); + + if (this.attributes.getNamedItem("skin")) + return; + + //e.relatedNode + var skinName, pNode = this.parentNode, skinNode; + if ((skinName = this.$canLeechSkin.dataType + == apf.STRING ? this.$canLeechSkin : this.localName) + && pNode.$originalNodes + && (skinNode = pNode.$originalNodes[skinName]) + && skinNode.getAttribute("inherit")) { + var link = skinNode.getAttribute("link"); + this.$isLeechingSkin = true; + if (link) { + this.$forceSkinChange(link); + } + else { + var skin = pNode.skinName.split(":"); + this.$forceSkinChange(skin[1], skin[0]); + } + } + else if (this.$isLeechingSkin) { + delete this.skin; + this.$isLeechingSkin = false; + this.$forceSkinChange(); + } + } + + //Skin Inheritance + //@todo Probably requires some cleanup + this.$initSkin = function(x){ + if (this.$canLeechSkin) { + this.addEventListener("DOMNodeInserted", setLeechedSkin); + } + + if (!this.skin) + this.skin = this.getAttribute("skin"); + + var skinName, pNode = this.parentNode, skinNode; + if (this.$canLeechSkin && !this.skin + && (skinName = this.$canLeechSkin.dataType == apf.STRING + ? this.$canLeechSkin + : this.localName) + && pNode && pNode.$originalNodes + && (skinNode = pNode.$originalNodes[skinName]) + && skinNode.getAttribute("inherit")) { + var link = skinNode.getAttribute("link"); + this.$isLeechingSkin = true; + if (link) { + this.skin = link; + this.$loadSkin(); + } + else { + this.$loadSkin(pNode.skinName); + } + } + else { + if (!this.skinset) + this.skinset = this.getAttribute("skinset"); + + this.$loadSkin(null, this.$canLeechSkin); + } + } + + /** + * Initializes the skin for this element when none has been set up. + * + * @param {String} skinName required Identifier for the new skin (for example: 'default:List' or 'win'). + * @param {Boolean} [noError] + */ + this.$loadSkin = function(skinName, noError){ + //this.skinName || //where should this go and why? + this.baseSkin = skinName || (this.skinset + || this.$setInheritedAttribute("skinset")) + + ":" + (this.skin || this.localName); + + clearTimeout(this.$skinTimer); + + if (this.skinName) { + this.$blur(); + this.$baseCSSname = null; + } + + this.skinName = this.baseSkin; //Why?? + //this.skinset = this.skinName.split(":")[0]; + + this.$pNodes = {}; //reset the this.$pNodes collection + this.$originalNodes = apf.skins.getTemplate(this.skinName, true); + + if (!this.$originalNodes) { + var skin = this.skin; + if (skin) { + var skinset = this.skinName.split(":")[0]; + this.baseName = this.skinName = "default:" + skin; + this.$originalNodes = apf.skins.getTemplate(this.skinName); + + if (!this.$originalNodes && skinset != "default") { + this.baseName = this.skinName = skinset + ":" + this.localName; + this.$originalNodes = apf.skins.getTemplate(this.skinName, true); + } + } + + if (!this.$originalNodes) { + this.baseName = this.skinName = "default:" + this.localName; + this.$originalNodes = apf.skins.getTemplate(this.skinName); + } + + if (!this.$originalNodes) { + if (noError) { + return (this.baseName = this.skinName = + this.originalNode = null); + } + + throw new Error(apf.formatErrorString(1077, this, + "Presentation", + "Could not load skin: " + this.baseSkin)); + } + + //this.skinset = this.skinName.split(":")[0]; + } + + if (this.$originalNodes) + apf.skins.setSkinPaths(this.skinName, this); + }; + + this.$getNewContext = function(type, amlNode){ + + + this.$pNodes[type] = this.$originalNodes[type].cloneNode(true); + }; + + this.$hasLayoutNode = function(type){ + + + return this.$originalNodes[type] ? true : false; + }; + + this.$getLayoutNode = function(type, section, htmlNode){ + + + var node = this.$pNodes[type] || this.$originalNodes[type]; + if (!node) { + + return false; + } + + if (!section) + return htmlNode || apf.getFirstElement(node); + + var textNode = node.getAttribute(section); + if (!textNode) + return null; + + return (htmlNode + ? apf.queryNode(htmlNode, textNode) + : apf.getFirstElement(node).selectSingleNode(textNode)); + }; + + this.$getOption = function(type, section){ + type = type.toLowerCase(); //HACK: lowercasing should be solved in the comps. + + //var node = this.$pNodes[type]; + var node = this.$pNodes[type] || this.$originalNodes[type]; + if (!section || !node) + return node;//apf.getFirstElement(node); + var option = node.selectSingleNode("@" + section); + + return option ? option.nodeValue : ""; + }; + + this.$getExternal = function(tag, pNode, func, aml){ + if (!pNode) + pNode = this.$pHtmlNode; + if (!tag) + tag = "main"; + //if (!aml) + //aml = this.$aml; + + tag = tag.toLowerCase(); //HACK: make components case-insensitive + + this.$getNewContext(tag); + var oExt = this.$getLayoutNode(tag); + + var node; + if (node = (aml || this).getAttributeNode("style")) + oExt.setAttribute("style", node.nodeValue); + + //if (node = (aml || this).getAttributeNode("class")) + //this.$setStyleClass(oExt, (oldClass = node.nodeValue)); + + if (func) + func.call(this, oExt); + + oExt = apf.insertHtmlNode(oExt, pNode); + oExt.host = this; + if (node = (aml || this).getAttributeNode("bgimage")) + oExt.style.backgroundImage = "url(" + apf.getAbsolutePath( + this.mediaPath, node.nodeValue) + ")"; + + if (!this.$baseCSSname) + this.$baseCSSname = oExt.className.trim().split(" ")[0]; + + return oExt; + }; + + /**** Focus ****/ + this.$focus = function(){ + if (!this.$ext) + return; + + this.$setStyleClass(this.oFocus || this.$ext, this.$baseCSSname + "Focus"); + }; + + this.$blur = function(){ + + if (this.renaming) + this.stopRename(null, true); + + + if (!this.$ext) + return; + + this.$setStyleClass(this.oFocus || this.$ext, "", [this.$baseCSSname + "Focus"]); + }; + + this.$fixScrollBug = function(){ + if (this.$int != this.$ext) + this.oFocus = this.$int; + else { + this.oFocus = + this.$int = + this.$ext.appendChild(document.createElement("div")); + + this.$int.style.height = "100%"; + this.$int.className = "focusbug" + } + }; + + /**** Caching ****/ + /* + this.$setClearMessage = function(msg){}; + this.$updateClearMessage = function(){} + this.$removeClearMessage = function(){};*/ +}).call(apf.Presentation.prototype = new apf.GuiElement()); + +apf.config.$inheritProperties["skinset"] = 1; + + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/core/baseclasses/validation.js)SIZE(27683)TIME(Fri, 14 Oct 2011 08:01:44 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + +apf.__VALIDATION__ = 1 << 6; + + + +//if checkequal then notnull = true +apf.validator = { + macro : { + + "datatype" : "apf.xsd.matchType(value, '", + "datatype_" : "')", + + + //var temp + "pattern" : "value.match(", + "pattern_" : ")", + "custom" : "(", + "custom_" : ")", + "min" : "parseInt(value) >= ", + "max" : "parseInt(value) <= ", + "maxlength" : "value.toString().length <= ", + "minlength" : "value.toString().length >= ", + "notnull" : "value.toString().length > 0", + "checkequal" : "!(temp = ", + "checkequal_" : ").isValid() || temp.getValue() == value" + }, + + compile : function(options){ + var m = this.macro, s = ["var temp, valid = true; \ + if (!validityState) \ + validityState = new apf.validator.validityState(); "]; + + if (options.required) { + s.push("if (checkRequired && (!value || value.toString().trim().length == 0)) {\ + validityState.$reset();\ + validityState.valueMissing = true;\ + valid = false;\ + }") + } + + s.push("validityState.$reset();\ + if (value) {"); + + for (prop in options) { + if (!m[prop]) continue; + s.push("if (!(", m[prop], options[prop], m[prop + "_"] || "", ")){\ + validityState.$set('", prop, "');\ + valid = false;\ + }"); + } + + s.push("};validityState.valid = valid; return validityState;"); + return new Function('value', 'checkRequired', 'validityState', s.join("")); + } +} + +/** + * Object containing information about the validation state. It contains + * properties that specify whether a certain validation was passed. + * Remarks: + * This is part of {@link http://www.whatwg.org/specs/web-apps/current-work/multipage/forms.html#validitystatethe HTML 5 specification}. + */ +apf.validator.validityState = function(){ + this.valueMissing = false, + this.typeMismatch = false, + this.patternMismatch = false, + this.tooLong = false, + this.rangeUnderflow = false, + this.rangeOverflow = false, + this.stepMismatch = false, + this.customError = false, + this.valid = true, + + this.$reset = function(){ + for (var prop in this) { + if (prop.substr(0,1) == "$") + continue; + this[prop] = false; + } + this.valid = true; + }, + + this.$set = function(type) { + switch (type) { + case "min" : this.rangeUnderflow = true; break; + case "max" : this.rangeOverflow = true; break; + case "minlength" : this.tooShort = true; break; + case "maxlength" : this.tooLong = true; break; + case "pattern" : this.patternMismatch = true; break; + case "datatype" : this.typeMismatch = true; break; + case "notnull" : this.typeMismatch = true; break; + case "checkequal" : this.typeMismatch = true; break; + } + } +}; + +/** + * All elements inheriting from this {@link term.baseclass baseclass} have validation support. + * Example: + * + * + * Number + * + * Name + * + * Message + * + * + * + * Validate + * + * + * + * + * @event invalid Fires when this component goes into an invalid state. + * + * @constructor + * @baseclass + * @author Ruben Daniels (ruben AT ajax DOT org) + * @version %I%, %G% + * @since 0.5 + */ +apf.Validation = function(){ + this.$regbase = this.$regbase | apf.__VALIDATION__; + + /** + * Checks if this element's value is valid. + * + * @param {Boolean} [checkRequired] whether this check also adheres to the 'required' ruled. + * @return {Boolean} specifying whether the value is valid + * @see baseclass.validationgroup + * @see element.submitform + */ + this.isValid = function(checkRequired){ + if (!this.$vOptions) + return true; + + (this.$vOptions.isValid || (this.$vOptions.isValid + = apf.validator.compile(this.$vOptions))).call(this, + typeof this.getValue == "function" ? this.getValue(null, true) : null, + checkRequired, this.validityState || + (this.validityState = new apf.validator.validityState())); + + var valid = this.validityState.valid; + + + + this.dispatchEvent(!valid ? "invalid" : "valid", this.validityState); + + return valid; + }; + + /** + * @private + */ + this.setCustomValidity = function(message){ + //do stuff + } + + /** + * @private + * @todo This method should also scroll the element into view + */ + this.showMe = function(){ + var p = this.parentNode; + while (p) { + if (p.show) + p.show(); + p = p.parentNode; + } + }; + + /** + * Puts this element in the error state, optionally showing the + * error box if this element's is invalid. + * + * @param {Boolean} [ignoreReq] whether this element required check is turned on. + * @param {Boolean} [nosetError] whether the error box is displayed if this component does not validate. + * @param {Boolean} [force] whether this element in the error state and don't check if the element's value is invalid. + * @return {Boolean} boolean specifying whether the value is valid + * @see object.validationgroup + * @see element.submitform + * @method + */ + + this.checkValidity = + + + /** + * Puts this element in the error state, optionally showing the + * error box if this element is invalid. + * + * @param {Boolean} [ignoreReq] whether this element required check is turned on. + * @param {Boolean} [nosetError] whether the error box is displayed if this component does not validate. + * @param {Boolean} [force] whether this element in the error state and don't check if the element's value is invalid. + * @return {Boolean} boolean specifying whether the value is valid + * @see object.validationgroup + * @see element.submitform + * @method + */ + this.validate = function(ignoreReq, nosetError, force){ + //if (!this.$validgroup) return this.isValid(); + + if (force || !this.isValid(!ignoreReq) && !nosetError) { + this.setError(); + return false; + } + else { + this.clearError(); + return true; + } + }; + + /** + * @private + */ + this.setError = function(value){ + if (!this.$validgroup) + this.$propHandlers["validgroup"].call(this, "vg" + this.parentNode.$uniqueId); + + var errBox = this.$validgroup.getErrorBox(this); + + if (!this.$validgroup.allowMultipleErrors) + this.$validgroup.hideAllErrors(); + + errBox.setMessage(this.invalidmsg || value); + + apf.setStyleClass(this.$ext, this.$baseCSSname + "Error"); + this.showMe(); //@todo scroll refHtml into view + + if (this.invalidmsg || value) + errBox.display(this); + + + if (this.hasFeature(apf.__MULTISELECT__) && this.validityState.$errorXml) + this.select(this.validityState.$errorXml); + + + if (apf.document.activeElement && apf.document.activeElement != this) + this.focus(null, {mouse:true}); //arguable... + }; + + /** + * @private + */ + this.clearError = function(value){ + if (this.$setStyleClass) + this.$setStyleClass(this.$ext, "", [this.$baseCSSname + "Error"]); + + if (this.$validgroup) { + var errBox = this.$validgroup.getErrorBox(null, true); + if (!errBox || errBox.host != this) + return; + + errBox.hide(); + } + }; + + this.addEventListener("DOMNodeRemovedFromDocument", function(e){ + if (this.$validgroup) + this.$validgroup.unregister(this); + }); + + /** + * + * @attribute {Boolean} required whether a valid value for this element is required. + * @attribute {RegExp} pattern the pattern tested against the value of this element to determine it's validity. + * @attribute {String} datatype the datatype that the value of this element should adhere to. This can be any + * of a set of predefined types, or a simple type created by an XML Schema definition. + * Possible values: + * {String} xsd:dateTime + * {String} xsd:time + * {String} xsd:date + * {String} xsd:gYearMonth + * {String} xsd:gYear + * {String} xsd:gMonthDay + * {String} xsd:gDay + * {String} xsd:gMonth + * {String} xsd:string + * {String} xsd:boolean + * {String} xsd:base64Binary + * {String} xsd:hexBinary + * {String} xsd:float + * {String} xsd:decimal + * {String} xsd:double + * {String} xsd:anyURI + * {String} xsd:integer + * {String} xsd:nonPositiveInteger + * {String} xsd:negativeInteger + * {String} xsd:long + * {String} xsd:int + * {String} xsd:short + * {String} xsd:byte + * {String} xsd:nonNegativeInteger + * {String} xsd:unsignedLong + * {String} xsd:unsignedInt + * {String} xsd:unsignedShort + * {String} xsd:unsignedByte + * {String} xsd:positiveInteger + * {String} apf:url + * {String} apf:website + * {String} apf:email + * {String} apf:creditcard + * {String} apf:expdate + * {String} apf:wechars + * {String} apf:phonenumber + * {String} apf:faxnumber + * {String} apf:mobile + * @attribute {Integer} min the minimal value for which the value of this element is valid. + * @attribute {Integer} max the maximum value for which the value of this element is valid. + * @attribute {Integer} minlength the minimal length allowed for the value of this element. + * @attribute {Integer} maxlength the maximum length allowed for the value of this element. + * @attribute {Boolean} notnull whether the value is filled. Same as {@link baseclass.validation.attribute.required} but this rule is checked realtime when the element looses the focus, instead of at specific request (for instance when leaving a form page). + * @attribute {String} checkequal the id of the element to check if it has the same value as this element. + * @attribute {String} invalidmsg the message displayed when this element has an invalid value. Use a ; character to seperate the title from the message. + * @attribute {String} validgroup the identifier for a group of items to be validated at the same time. This identifier can be new. It is inherited from a AML node upwards. + * @attribute {String} validtest the instruction on how to test for success. This attribute is generally used to check the value on the server. + * Example: + * This example shows how to check the username on the server. In this case + * comm.loginCheck is an async rpc function that checks the availability of the + * username. If it exists it will return 0, otherwise 1. The value variable + * contains the current value of the element (in this case the textbox). It + * can be used as a convenience variable. + *
+     *  Username
+     *  
+     * 
+ */ + this.addEventListener("DOMNodeInsertedIntoDocument", function(e){ + //this.addEventListener(this.hasFeature(apf.__MULTISELECT__) ? "onafterselect" : "onafterchange", onafterchange); + /* Temp disabled, because I don't understand it (RLD) + this.addEventListener("beforechange", function(){ + if (this.xmlRoot && apf.getBoundValue(this) === this.getValue()) + return false; + });*/ + + // validgroup + if (!this.validgroup) + this.$setInheritedAttribute("validgroup"); + }); + + //1 = force no bind rule, 2 = force bind rule + this.$attrExcludePropBind = apf.extend({ + pattern : 1, + validtest : 3 + }, this.$attrExcludePropBind); + + this.$booleanProperties["required"] = true; + this.$supportedProperties.push("validgroup", "required", "datatype", + "pattern", "min", "max", "maxlength", "minlength", "validtest", + "notnull", "checkequal", "invalidmsg", "requiredmsg"); + + this.$fValidate = function(){ + if (this.liveedit) + return; + + if (!this.$validgroup) + this.validate(true); + else { + var errBox = this.$validgroup.getErrorBox(this); + if (!errBox.visible || errBox.host != this) + this.validate(true); + } + }; + this.addEventListener("blur", this.$fValidate); + + this.$propHandlers["validgroup"] = function(value){ + if (value) { + var vgroup; + if (typeof value != "string") { + this.$validgroup = value.name; + vgroup = value; + } + else { + + vgroup = apf.nameserver.get("validgroup", value); + + } + + this.$validgroup = vgroup || new apf.ValidationGroup(value); + this.$validgroup.register(this); + /* + @todo What about children, when created after start + See button login action + */ + } + else { + this.$validgroup.unregister(this); + this.$validgroup = null; + } + }; + + this.$propHandlers["pattern"] = function(value, prop){ + if (value.substr(0, 1) != "/") + value = "/" + value + "/"; + + (this.$vOptions || (this.$vOptions = {}))[prop] = value; + delete this.$vOptions.isValid; + }; + + + this.$propHandlers["datatype"] = + + this.$propHandlers["required"] = + this.$propHandlers["custom"] = + this.$propHandlers["min"] = + this.$propHandlers["max"] = + this.$propHandlers["maxlength"] = + this.$propHandlers["minlength"] = + this.$propHandlers["notnull"] = + this.$propHandlers["checkequal"] = function(value, prop){ + (this.$vOptions || (this.$vOptions = {}))[prop] = value; + delete this.$vOptions.isValid; + }; + + //@todo rewrite this for apf3.0 - it should just execute a live markup + this.$propHandlers["validtest"] = function(value){ + var _self = this, rvCache = {}; + /** + * Removes the validation cache created by the validtest rule. + */ + this.removeValidationCache = function(){ + rvCache = {}; + } + + this.$checkRemoteValidation = function(){ + var value = this.getValue(); + if(typeof rvCache[value] == "boolean") return rvCache[value]; + if(rvCache[value] == -1) return true; + rvCache[value] = -1; + + apf.getData(this.validtest.toString(), { + xmlNode : this.xmlRoot, + value : this.getValue(), + callback : function(data, state, extra){ + if (state != apf.SUCCESS) { + if (state == apf.TIMEOUT && extra.retries < apf.maxHttpRetries) + return extra.tpModule.retry(extra.id); + else { + var commError = new Error(apf.formatErrorString(0, _self, + "Validating entry at remote source", + "Communication error: \n\n" + extra.message)); + + if (_self.dispatchEvent("error", apf.extend({ + error : commError, + state : status + }, extra)) !== false) + throw commError; + return; + } + } + + rvCache[value] = apf.isTrue(data);//instr[1] ? data == instr[1] : apf.isTrue(data); + + if(!rvCache[value]){ + if (!_self.hasFocus()) + _self.setError(); + } + else _self.clearError(); + } + }); + + return true; + }; + + (this.$vOptions || (this.$vOptions = {})).custom = "apf.lookup(" + this.$uniqueId + ").$checkRemoteValidation()"; + delete this.$vOptions.isValid; + }; +}; + + +apf.GuiElement.propHandlers["datatype"] = + +apf.GuiElement.propHandlers["required"] = +apf.GuiElement.propHandlers["pattern"] = +apf.GuiElement.propHandlers["min"] = +apf.GuiElement.propHandlers["max"] = +apf.GuiElement.propHandlers["maxlength"] = +apf.GuiElement.propHandlers["minlength"] = +apf.GuiElement.propHandlers["notnull"] = +apf.GuiElement.propHandlers["checkequal"] = +apf.GuiElement.propHandlers["validtest"] = function(value, prop){ + this.implement(apf.Validation); + this.$propHandlers[prop].call(this, value, prop); +} + +/** + * Object allowing for a set of AML elements to be validated, an element that + * is not valid shows the errorbox. + * Example: + * + * + * Phone number + * + * + * Password + * + * + * + * + * To check if the element has valid information entered, leaving the textbox + * (focussing another element) will trigger a check. Programmatically a check + * can be done using the following code: + * + * txtPhone.validate(); + * + * //Or use the html5 syntax + * txtPhone.checkValidity(); + * + * + * To check for the entire group of elements use the validation group. For only + * the first non-valid element the errorbox is shown. That element also receives + * focus. + * + * vgForm.validate(); + * + * + * @event validation Fires when the validation group isn't validated. + * + * @inherits apf.Class + * @constructor + * @default_private + * + * @author Ruben Daniels (ruben AT ajax DOT org) + * @version %I%, %G% + * @since 0.9 + */ +apf.ValidationGroup = function(name){ + this.$init(); + + this.childNodes = []; + + if (name) + apf.setReference(name, this); + + this.name = name || "validgroup" + this.$uniqueId; + + apf.nameserver.register("validgroup", this.name, this); + +}; + +(function(){ + /** + * When set to true, only visible elements are validated. Default is false. + * @type Boolean + */ + this.validateVisibleOnly = false; + + /** + * When set to true, validation doesn't stop at the first invalid element. Default is false. + * @type Boolean + */ + this.allowMultipleErrors = false; + + /** + * Adds a aml element to this validation group. + */ + this.register = function(o){ + if (o.hasFeature(apf.__VALIDATION__)) + this.childNodes.push(o); + }; + + /** + * Removes a aml element from this validation group. + */ + this.unregister = function(o){ + this.childNodes.remove(o); + }; + + /** + * Returns a string representation of this object. + */ + this.toString = function(){ + return "[APF Validation Group]"; + }; + + //Shared among all validationgroups + var errbox; + /** + * Retrieves {@link element.errorbox} used for a specified element. + * + * @param {AmlNode} o required AmlNode specifying the element for which the Errorbox should be found. If none is found, an Errorbox is created. Use the {@link object.validationgroup.property.allowMultipleErrors} to influence when Errorboxes are created. + * @param {Boolean} no_create Boolean that specifies whether new Errorbox may be created when it doesn't exist already + * @return {Errorbox} the found or created Errorbox; + */ + this.getErrorBox = function(o, no_create){ + if (this.allowMultipleErrors || !errbox && !no_create) { + errbox = new apf.errorbox(); + errbox.$pHtmlNode = o.$ext.parentNode; + errbox.skinset = apf.getInheritedAttribute(o.parentNode, "skinset"); //@todo use skinset here. Has to be set in presentation + errbox.dispatchEvent("DOMNodeInsertedIntoDocument"); + } + return errbox; + }; + + /** + * Hide all Errorboxes for the elements using this element as their validation group. + * + */ + this.hideAllErrors = function(){ + if (errbox && errbox.host) + errbox.host.clearError(); + }; + + function checkValidChildren(oParent, ignoreReq, nosetError){ + var found; + //Per Element + for (var v, i = 0; i < oParent.childNodes.length; i++) { + var oEl = oParent.childNodes[i]; + + if (!oEl) + continue; + if (!oEl.disabled + && (!this.validateVisibleOnly && oEl.visible || !oEl.$ext || oEl.$ext.offsetHeight) + && (oEl.hasFeature(apf.__VALIDATION__) && oEl.isValid && !oEl.isValid(!ignoreReq))) { + //|| !ignoreReq && oEl.required && (!(v = oEl.getValue()) || new String(v).trim().length == 0) + + if (!nosetError) { + if (!found) { + oEl.validate(true, null, true); + found = true; + if (!this.allowMultipleErrors) + return true; //Added (again) + } + else if (oEl.errBox && oEl.errBox.host == oEl) + oEl.errBox.hide(); + } + else if (!this.allowMultipleErrors) + return true; + } + if (oEl.canHaveChildren && oEl.childNodes.length) { + found = checkValidChildren.call(this, oEl, ignoreReq, nosetError) || found; + if (found && !this.allowMultipleErrors) + return true; //Added (again) + } + } + return found; + } + + /** + * Checks if (part of) the set of element's registered to this element are + * valid. When an element is found with an invalid value the error state can + * be set for that element. + * + * @param {Boolean} [ignoreReq] whether to adhere to the 'required' check. + * @param {Boolean} [nosetError whether to not set the error state of the element with an invalid value + * @param {AMLElement} [page] the page for which the children will be checked. When not specified all elements of this validation group will be checked. + * @return {Boolean} specifying whether the checked elements are valid. + * @method isValid, validate, checkValidity + */ + + this.checkValidity = + + + /** + * Checks if (part of) the set of element's registered to this element are + * valid. When an element is found with an invalid value the error state can + * be set for that element. + * + * @param {Boolean} [ignoreReq] whether to adhere to the 'required' check. + * @param {Boolean} [nosetError whether to not set the error state of the element with an invalid value + * @param {AMLElement} [page] the page for which the children will be checked. When not specified all elements of this validation group will be checked. + * @return {Boolean} specifying whether the checked elements are valid. + * @method isValid, validate, checkValidity + */ + this.validate = + + /** + * Checks if (part of) the set of element's registered to this element are + * valid. When an element is found with an invalid value the error state can + * be set for that element. + * + * @param {Boolean} [ignoreReq] whether to adhere to the 'required' check. + * @param {Boolean} [nosetError whether to not set the error state of the element with an invalid value + * @param {AMLElement} [page] the page for which the children will be checked. When not specified all elements of this validation group will be checked. + * @return {Boolean} specifying whether the checked elements are valid. + * @method isValid, validate, checkValidity + */ + this.isValid = function(ignoreReq, nosetError, page){ + var found = checkValidChildren.call(this, page || this, ignoreReq, + nosetError); + + if (page) { + + if (page.validation && !eval(page.validation)) { + alert(page.invalidmsg); + found = true; + } + + } + + //Global Rules + // + //if (!found) + //found = this.dispatchEvent("validation"); + + return !found; + }; +}).call(apf.ValidationGroup.prototype = new apf.Class()); + +apf.config.$inheritProperties["validgroup"] = 1; + + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/core/baseclasses/databinding.js)SIZE(58833)TIME(Tue, 13 Dec 2011 13:33:58 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + +apf.__DATABINDING__ = 1 << 1; + + + +/** + * Baseclass adding data binding features to this element. Databinding takes + * care of automatically going from data to representation and establishing a + * permanent link between the two. In this way data that is changed will + * change the representation as well. Furthermore, actions that are executed on + * the representation will change the underlying data. + * Example: + * + * + * + * + * Item 1 + * Item 2 + * + * + * + * + * + * + * + * + * + * + * @event error Fires when a communication error has occured while + * making a request for this element. + * cancelable: Prevents the error from being thrown. + * bubbles: + * object: + * {Error} error the error object that is thrown when the event + * callback doesn't return false. + * {Number} state the state of the call + * cancellable: Prevents the error from being thrown. + * Possible values: + * apf.SUCCESS the request was successfull + * apf.TIMEOUT the request has timed out. + * apf.ERROR an error has occurred while making the request. + * apf.OFFLINE the request was made while the application was offline. + * {mixed} userdata data that the caller wanted to be available in + * the callback of the http request. + * {XMLHttpRequest} http the object that executed the actual http request. + * {String} url the url that was requested. + * {Http} tpModule the teleport module that is making the request. + * {Number} id the id of the request. + * {String} message the error message. + * @event beforeretrieve Fires before a request is made to retrieve data. + * cancelable: Prevents the data from being retrieved. + * @event afterretrieve Fires when the request to retrieve data returns both + * on success and failure. + * @event receive Fires when data is successfully retrieved + * object: + * {String} data the retrieved data + * + * @constructor + * @baseclass + * @author Ruben Daniels (ruben AT ajax DOT org) + * @version %I%, %G% + * @since 0.4 + * @default_private + */ +apf.DataBinding = function(){ + this.$init(true); + + this.$loadqueue = + this.$dbTimer = null; + this.$regbase = this.$regbase | apf.__DATABINDING__; + this.$mainBind = "value"; + + this.$bindings = + this.$cbindings = + this.$attrBindings = false; + + //1 = force no bind rule, 2 = force bind rule + this.$attrExcludePropBind = apf.extend({ + model : 1, + each : 1 + //eachvalue : 1 //disabled because of line 1743 valueRule = in multiselect.js + }, this.$attrExcludePropBind); + + /**** Public Methods ****/ + + /** + * Sets a value of an XMLNode based on an xpath statement executed on the data of this model. + * + * @param {String} xpath the xpath used to select a XMLNode. + * @param {String} value the value to set. + * @return {XMLNode} the changed XMLNode + */ + this.setQueryValue = function(xpath, value, type){ + var node = apf.createNodeFromXpath(this[type || 'xmlRoot'], xpath); + if (!node) + return null; + + apf.setNodeValue(node, value, true); + //apf.xmldb.setTextNode(node, value); + return node; + }; + + /** + * Queries the bound data for a string value + * + * @param {String} xpath the xpath statement which queries on the data this element is bound on. + * @param {String} type the node that is used as the context node for the query. + * Possible values: + * selected the selected data anode of this element. + * xmlRoot the root data node that this element is bound on. + * indicator the data node that is highlighted for keyboard navigation. + * @return {String} value of the selected XML Node + * @todo + * lstRev.query('revision/text()', 'selected'); + * lstRev.query('revision/text()', 'xmlRoot'); + * lstRev.query('revision/text()', 'indicator'); + */ + this.queryValue = function(xpath, type){ + return apf.queryValue(this[type || 'xmlRoot'], xpath ); + }; + /** + * Queries the bound data for an array of string values + * + * @param {String} xpath the xpath statement which queries on the data this element is bound on. + * @param {String} type the node that is used as the context node for the query. + * Possible values: + * selected the selected data anode of this element. + * xmlRoot the root data node that this element is bound on. + * indicator the data node that is highlighted for keyboard navigation. + * @return {String} value of the selected XML Node + */ + this.queryValues = function(xpath, type){ + return apf.queryValues(this[type || 'xmlRoot'], xpath ); + }; + + /** + * Executes an xpath statement on the data of this model + * + * @param {String} xpath the xpath used to select the XMLNode(s). + * @param {String} type the node that is used as the context node for the query. + * Possible values: + * selected the selected data anode of this element. + * xmlRoot the root data node that this element is bound on. + * indicator the data node that is highlighted for keyboard navigation. + * @return {variant} XMLNode or NodeList with the result of the selection + */ + this.queryNode = function(xpath, type){ + var n = this[type||'xmlRoot']; + return n ? n.selectSingleNode(xpath) : null; + }; + + /** + * Executes an xpath statement on the data of this model + * + * @param {String} xpath the xpath used to select the XMLNode(s). + * @param {String} type the node that is used as the context node for the query. + * Possible values: + * selected the selected data anode of this element. + * xmlRoot the root data node that this element is bound on. + * indicator the data node that is highlighted for keyboard navigation. + * @return {variant} XMLNode or NodeList with the result of the selection + */ + this.queryNodes = function(xpath, type){ + var n = this[type||'xmlRoot']; + return n ? n.selectNodes(xpath) : []; + }; + + this.$checkLoadQueue = function(){ + // Load from queued load request + if (this.$loadqueue) { + if (!this.caching) + this.xmlRoot = null; + var q = this.load(this.$loadqueue[0], {cacheId: this.$loadqueue[1]}); + if (!q || q.dataType != apf.ARRAY || q != this.$loadqueue) + this.$loadqueue = null; + } + else return false; + }; + + //setProp + this.$execProperty = function(prop, xmlNode, undoObj){ + var attr = this.$attrBindings[prop]; + + //@todo this is a hacky solution for replaceNode support - Have to rethink this. + if (this.nodeType == 7) { + if (xmlNode != this.xmlRoot) + this.xmlRoot = xmlNode; + } + + + + + + + try { + + if (attr.cvalue.asyncs) { //if async + var _self = this; + return attr.cvalue.call(this, xmlNode, function(value){ + _self.setProperty(prop, value, true); + + + + }); + } + else { + var value = attr.cvalue.call(this, xmlNode); + } + + } + catch(e){ + apf.console.warn("[400] Could not execute binding for property " + + prop + "\n\n" + e.message); + return; + } + + + this.setProperty(prop, undoObj && undoObj.extra.range || value, true); //@todo apf3.0 range + + + }; + + //@todo apf3.0 contentEditable support + this.$applyBindRule = function(name, xmlNode, defaultValue, callback, oHtml){ + var handler = this.$attrBindings[name] + && this.$attrBindings[name].cvalue || this.$cbindings[name]; + + return handler ? handler.call(this, xmlNode, callback) : defaultValue || ""; + }; + + + this.addEventListener("$clear", function(){ + var queue; + if (!this.$amlBindQueue || !(queue = this.$amlBindQueue.caption)) + return; + + for (var id in queue) { + var lm = queue[id]; + if (lm.destroy) { + lm.parentNode = lm.$focusParent = lm.$model = lm.xmlRoot = null; + lm.destroy(); + } + delete queue[id]; + } + + //delete this.$amlBindQueue; + }); + + var afterloadUpdate; + this.addEventListener("afterload", afterloadUpdate = function(){ + var queue; + if (!this.$amlBindQueue + || !(queue = this.$amlBindQueue.caption || this.$amlBindQueue.column)) + return; + + var div, doc = this.ownerDocument; + for (var lm, i = 0, l = queue.length; i < l; i++) { + if (!queue[i]) continue; //@todo check out why this happens + + div = document.getElementById("placeholder_" + + this.$uniqueId + "_" + i); + + lm = doc.createProcessingInstruction("lm", this.$cbindings.caption + || this.$cbindings.column); + lm.$model = this.$model; + lm.xmlRoot = queue[i]; //xmlNode + lm.$noInitModel = true; + //lm.$useXmlDiff = true; + lm.$focusParent = this; + lm.parentNode = this; + lm.dispatchEvent("DOMNodeInsertedIntoDocument", { + pHtmlNode: div + }); + queue[lm.xmlRoot.getAttribute(apf.xmldb.xmlIdTag)] = lm; + delete queue[i]; + } + + queue.length = 0; + }); + + //Add and remove handler + this.addEventListener("xmlupdate", function(e){ + if (e.xmlNode != e.traverseNode) //@todo this should be more specific + return; + + if ("insert|add|synchronize|move".indexOf(e.action) > -1) + afterloadUpdate.call(this, e); + else if ("remove|move-away".indexOf(e.action) > -1) { + var queue; + if (!this.$amlBindQueue || !(queue = this.$amlBindQueue.caption)) + return; + + /*var htmlNode = apf.xmldb.findHtmlNode(e.xmlNode, this); + var captionNode = this.$getLayoutNode("caption", htmlNode); + var id = captionNode.getAttribute("id").split("_")[2]; + var lm = queue[id];*/ + var lm = queue[e.xmlNode.getAttribute(apf.xmldb.xmlIdTag)]; + lm.parentNode = lm.$focusParent = lm.$model = lm.xmlRoot = null; + lm.destroy(); + } + }); + + + this.$hasBindRule = function(name){ + return this.$attrBindings[name] || this.$bindings + && this.$bindings[name]; + }; + + this.$getBindRule = function(name, xmlNode){ + return this.$attrBindings[name] || this.$bindings + && this.$bindings.getRule(name, xmlNode); + }; + + var ruleIsMatch = {"drag":1,"drop":1,"dragcopy":1} + this.$getDataNode = function(name, xmlNode, createNode, ruleList, multiple){ + var node, rule = this.$attrBindings[name]; + if (rule) { //@todo apf3.0 find out why drag and drop rules are already compiled here + if (rule.cvalue.type != 3) //@todo warn here? + return false; + + var func = rule.cvalue2 || rule.compile("value", { + xpathmode : multiple ? 4 : 3, + parsecode : 1, + injectself : ruleIsMatch[name] + }); + if (func && (node = func(xmlNode, createNode))) { + if (ruleList) + ruleList.push(rule); + + return node; + } + + return false; + } + + return this.$bindings + && this.$bindings.getDataNode(name, xmlNode, createNode, ruleList, multiple); + }; + + /** + * Sets the model of the specified element + * + * @param {Model} The model this element is going to connect to. + * + */ + + this.setModel = function(model){ + this.setAttribute("model", model, false, true); + }; + + + /** + * Gets the model to which this element is connected. + * This is the model which acts as a datasource for this element. + * + * @param {Boolean} doRecur whether the model should be searched recursively up the data tree. + * @returns {Model} The model this element is connected to. + * @see element.smartbinding + */ + this.getModel = function(doRecur){ + if (doRecur && !this.$model) + return this.dataParent ? this.dataParent.parent.getModel(true) : null; + + return this.$model; + }; + + /** + * Reloads the data in this element. + * + */ + this.reload = this.reload || function(){ + this.load(this.xmlRoot, {cacheId: this.cacheId, force: true}); + }; + + /** + * Loads data in to this element using binding rules to transform the + * data in to a presentation. + * Example: + * + * + * + * + * + * + * + * + * + * + * + * + * @param {mixed} [xmlNode] + * Possible Values: + * {XMLElement} an xml element loaded in to this element. + * {String} an xml string which is loaded in this element. + * {String} an instruction to load data from a remote source. + * {Null} null clears this element from it's data {@link baseclass.cache.method.clear}. + * @param {Object} [options] + * Properties: + * {XMLElement} [xmlNode] the {@link term.datanode data node} that provides + * context to the data instruction. + * {Function} [callback] the code executed when the data request returns. + * {mixed} [] Custom properties available in the data instruction. + * {String} [cacheId] the xml element to which the binding rules are applied. + * {Boolean} [force] whether cache is checked before loading the data. + * {Boolean} [noClearMsg] wether a message is set when clear is called. + * + * @event beforeload Fires before loading data in this element. + * cancelable: Prevents the data from being loaded. + * object: + * {XMLElement} xmlNode the node that is loaded as the root {@link term.datanode data node}. + * @event afterload Fires after loading data in this element. + * object: + * {XMLElement} xmlNode the node that is loaded as the root {@link term.datanode data node}. + * @see element.smartbinding + * @see baseclass.cache.method.clear + */ + this.load = function(xmlNode, options){ + if (options) { + var cacheId = options.cacheId, + forceNoCache = options.force, + noClearMsg = options.noClearMsg; + } + if (cacheId && cacheId == this.cacheId && !forceNoCache) + return; + + + if (apf.popup.isShowing(this.$uniqueId)) + apf.popup.forceHide(); //This should be put in a more general position + + + // Convert first argument to an xmlNode we can use; + if (xmlNode) { + if (typeof xmlNode == "string") { + if (xmlNode.charAt(0) == "<") + xmlNode = apf.getXmlDom(xmlNode).documentElement; + else { + return apf.model.prototype.$loadFrom.call(this, xmlNode, options); + } + } + else if (xmlNode.nodeType == 9) { + xmlNode = xmlNode.documentElement; + } + else if (xmlNode.nodeType == 3 || xmlNode.nodeType == 4) { + xmlNode = xmlNode.parentNode; + } + else if (xmlNode.nodeType == 2) { + xmlNode = xmlNode.ownerElement + || xmlNode.parentNode + || xmlNode.selectSingleNode(".."); + } + } + + // If control hasn't loaded databinding yet, queue the call + if (this.$preventDataLoad || !this.$canLoadData + && ((!this.$bindings && (!this.$canLoadDataAttr || !this.each)) || !this.$amlLoaded) + && (!this.hasFeature(apf.__MULTISELECT__) || !this.each) + || this.$canLoadData && !this.$canLoadData()) { + + if (!this.caching || !this.hasFeature(apf.__CACHE__)) { + + //@todo this is wrong. It is never updated when there are only + //Property binds and then it just leaks xml nodes + this.xmlRoot = xmlNode; + + + this.setProperty("root", this.xmlRoot); + + } + + + + return this.$loadqueue = [xmlNode, cacheId]; + } + this.$loadqueue = null; + + // If no xmlNode is given we clear the control, disable it and return + if (this.dataParent && this.dataParent.xpath) + this.dataParent.parent.signalXmlUpdate[this.$uniqueId] = !xmlNode; + + if (!xmlNode && (!cacheId || !this.$isCached || !this.$isCached(cacheId))) { + + + this.clear(noClearMsg); + + + if (apf.config.autoDisable && this.$createModel === false) + this.setProperty("disabled", true); + + //@todo apf3.0 remove , true in clear above + //this.setProperty("selected", null); + + return; + } + + // If reloading current document, and caching is disabled, exit + if (!this.caching && !forceNoCache && xmlNode + && !this.$loadqueue && xmlNode == this.xmlRoot) + return; + + var disabled = this.disabled; + this.disabled = false; + + //Run onload event + if (this.dispatchEvent("beforeload", {xmlNode : xmlNode}) === false) + return false; + + + + this.clear(true, true); + + this.cacheId = cacheId; + + if (this.dispatchEvent("$load", { + forceNoCache : forceNoCache, + xmlNode : xmlNode + }) === false) { + //delete this.cacheId; + return; + } + + //Set usefull vars + this.documentId = apf.xmldb.getXmlDocId(xmlNode); + this.xmlRoot = xmlNode; + + + this.setProperty("root", this.xmlRoot); + + + + + // Draw Content + this.$load(xmlNode); + + + + // Check if subtree should be loaded + this.$loadSubData(xmlNode); + + if (this.$createModel === false) { + this.disabled = true; + this.setProperty("disabled", false); + } + else + this.disabled = disabled; + + // Run onafteronload event + this.dispatchEvent('afterload', {xmlNode : xmlNode}); + }; + + /** + * @binding load Determines how new data is loaded data is loaded into this + * element. Usually this is only the root node containing no children. + * Example: + * This example shows a load rule in a text element. It gets its data from + * a list. When a selection is made on the list the data is loaded into the + * text element. + * + * + * + * + * + * + * + * + * + * message 1 + * message 2 + * + * + * + * + * + * + * + * + * + * + * + * @attribute {string} get the {@link term.datainstruction data instruction} + * that is used to load data into the xmlRoot of this component. + */ + this.$loadSubData = function(xmlRootNode){ + if (this.$hasLoadStatus(xmlRootNode) && !this.$hasLoadStatus(xmlRootNode, "potential")) + return; + + //var loadNode = this.$applyBindRule("load", xmlRootNode); + var rule = this.$getBindRule("load", xmlRootNode); + if (rule && (!rule[1] || rule[1](xmlRootNode))) { + + + this.$setLoadStatus(xmlRootNode, "loading"); + + if (this.$setClearMessage) + this.$setClearMessage(this["loading-message"], "loading"); + + //||apf.xmldb.findModel(xmlRootNode) + var mdl = this.getModel(true); + + + var amlNode = this; + if (mdl.$insertFrom(rule.getAttribute("get"), { + xmlNode : xmlRootNode, //@todo apf3.0 + insertPoint : xmlRootNode, //this.xmlRoot, + amlNode : this, + callback : function(){ + + amlNode.setProperty(amlNode.hasFeature(apf.__MULTISELECT__) + ? "selected" + : "root", xmlRootNode); + + } + }) === false + ) { + this.clear(true); + + if (apf.config.autoDisable) + this.setProperty("disabled", true); + + //amlNode.setProperty("selected", null); //@todo is this not already done in clear? + + } + } + }; + + /** + * Unloads data from this element and resets state displaying an empty message. + * Empty message is set on the {@link baseclass.guielement.property.msg}. + * + * @param {Boolean} [nomsg] whether to display the empty message. + * @param {Boolean} [doEvent] whether to sent select events. + * @see baseclass.databinding.method.load + * @private + */ + //@todo this function is called way too much for a single load of a tree + //@todo should clear listener + this.clear = function(nomsg, doEvent, fakeClear){ + if (!this.$container) + return;//@todo apf3.0 + + if (this.clearSelection) + this.clearSelection(true);//!doEvent);//@todo move this to the $clear event in multiselect.js + + var lastHeight = this.$container.offsetHeight; + + if (this.dispatchEvent("$clear") !== false) + this.$container.innerHTML = ""; //@todo apf3.0 + + if (typeof nomsg == "string") { + var msgType = nomsg; + nomsg = false; + + //@todo apf3.0 please use attr. inheritance + if (!this[msgType + "-message"]) { + this.$setInheritedAttribute(msgType + "-message"); + } + } + this.$lastClearType = msgType || null; + + if (!nomsg && this.$setClearMessage) { + this.$setClearMessage(msgType + ? this[msgType + "-message"] + : this["empty-message"], msgType || "empty", lastHeight); + + //this.setProperty("selected", null); //@todo apf3.0 get the children to show loading... as well (and for each selected, null + //c[i].o.clear(msgType, doEvent); + } + else if(this.$removeClearMessage) + this.$removeClearMessage(); + + if (!fakeClear) + this.documentId = this.xmlRoot = this.cacheId = null; + + + if (!nomsg) { + if (this.hasFeature(apf.__MULTISELECT__)) //@todo this is all wrong + this.setProperty("length", 0); + //else + //this.setProperty("value", ""); //@todo redo apf3.0 + } + + }; + + this.clearMessage = function(msg){ + this.customMsg = msg; + this.clear("custom"); + }; + + /** + * @private + */ + //@todo optimize this + this.$setLoadStatus = function(xmlNode, state, remove){ + var group = this.loadgroup || "default"; + var re = new RegExp("\\|(\\w+)\\:" + group + ":(\\d+)\\|"); + var loaded = xmlNode.getAttribute("a_loaded") || ""; + + var m; + if (!remove && (m = loaded.match(re)) && m[1] != "potential" && m[2] != this.$uniqueId) + return; + + //remove old status if any + var ostatus = loaded.replace(re, "") + if (!remove) + ostatus += "|" + state + ":" + group + ":" + this.$uniqueId + "|"; + + xmlNode.setAttribute("a_loaded", ostatus); + }; + + /** + * @private + */ + this.$removeLoadStatus = function(xmlNode){ + this.$setLoadStatus(xmlNode, null, true); + }; + + /** + * @private + */ + this.$hasLoadStatus = function(xmlNode, state, unique){ + if (!xmlNode) + return false; + var ostatus = xmlNode.getAttribute("a_loaded"); + if (!ostatus) + return false; + + var group = this.loadgroup || "default"; + var re = new RegExp("\\|" + (state || "\\w+") + ":" + group + ":" + (unique ? this.$uniqueId : "\\d+") + "\\|"); + return ostatus.match(re) ? true : false; + }; + + /** + * @event beforeinsert Fires before data is inserted. + * cancelable: Prevents the data from being inserted. + * object: + * {XMLElement} xmlParentNode the parent in which the new data is inserted + * @event afterinsert Fires after data is inserted. + */ + + /** + * @private + */ + this.insert = function(xmlNode, options){ + if (typeof xmlNode == "string") { + if (xmlNode.charAt(0) == "<") { + + if (options.whitespace === false) + xmlNode = xmlNode.replace(/>[\s\n\r]*<"); + + xmlNode = apf.getXmlDom(xmlNode).documentElement; + } + else { + if (!options.insertPoint) + options.insertPoint = this.xmlRoot; + return apf.model.prototype.$insertFrom.call(this, xmlNode, options); + } + } + + var insertPoint = options.insertPoint || this.xmlRoot; + + if (this.dispatchEvent("beforeinsert", { + xmlParentNode : insertPoint + }) === false) + return false; + + //Integrate XMLTree with parentNode + if (typeof options.copyAttributes == "undefined") + options.copyAttributes = true; + + var newNode = apf.mergeXml(xmlNode, insertPoint, options); + + //Call __XMLUpdate on all listeners + apf.xmldb.applyChanges("insert", insertPoint); + + //Select or propagate new data + if (this.selectable && this.autoselect) { + if (this.xmlNode == newNode) + this.$selectDefault(this.xmlNode); + } + + else if (this.xmlNode == newNode) { + this.setProperty("root", this.xmlNode); + } + + + if (this.$hasLoadStatus(insertPoint, "loading")) + this.$setLoadStatus(insertPoint, "loaded"); + + this.dispatchEvent("afterinsert"); + + //Check Connections + //this one shouldn't be called because they are listeners anyway...(else they will load twice) + //if(this.selected) this.setConnections(this.selected, "select"); + }; + + /** + * @attribute {Boolean} render-root whether the xml element loaded into this + * element is rendered as well. Default is false. + * Example: + * This example shows a tree which also renders the root element. + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + */ + this.$booleanProperties["render-root"] = true; + this.$supportedProperties.push("empty-message", "loading-message", + "offline-message", "render-root", "smartbinding", + "bindings", "actions"); + + /** + * @attribute {Boolean} render-root wether the root node of the data loaded + * into this element is rendered as well. + * @see element.tree + */ + this.$propHandlers["render-root"] = function(value){ + this.renderRoot = value; + }; + + /** + * @attribute {String} empty-message the message displayed by this element + * when it contains no data. This property is inherited from parent nodes. + * When none is found it is looked for on the appsettings element. Otherwise + * it defaults to the string "No items". + */ + this.$propHandlers["empty-message"] = function(value){ + this["empty-message"] = value; + + if (this.$updateClearMessage) + this.$updateClearMessage(this["empty-message"], "empty"); + }; + + /** + * @attribute {String} loading-message the message displayed by this + * element when it's loading. This property is inherited from parent nodes. + * When none is found it is looked for on the appsettings element. Otherwise + * it defaults to the string "Loading...". + * Example: + * This example uses property binding to update the loading message. The + * position of the progressbar should be updated by the script taking care + * of loading the data. + * + * + * + * + * Remarks: + * Usually a static loading message is displayed for only 100 milliseconds + * or so, whilst loading the data from the server. This is done for instance + * when the load binding rule is used. In the code example below a list + * binds on the selection of a tree displaying folders. When the selection + * changes, the list loads new data by extending the model. During the load + * of this new data the loading message is displayed. + * + * + * + * ... + * + * + * + * + */ + this.$propHandlers["loading-message"] = function(value){ + this["loading-message"] = value; + + if (this.$updateClearMessage) + this.$updateClearMessage(this["loading-message"], "loading"); + }; + + /** + * @attribute {String} offline-message the message displayed by this + * element when it can't load data because the application is offline. + * This property is inherited from parent nodes. When none is found it is + * looked for on the appsettings element. Otherwise it defaults to the + * string "You are currently offline...". + */ + this.$propHandlers["offline-message"] = function(value){ + this["offline-message"] = value; + + if (this.$updateClearMessage) + this.$updateClearMessage(this["offline-message"], "offline"); + }; + + /** + * @attribute {String} smartbinding the name of the SmartBinding for this + * element. A smartbinding is a collection of rules which define how data + * is transformed into representation, how actions on the representation are + * propagated to the data and it's original source, how drag&drop actions + * change the data and where the data is loaded from. Each of these are + * optionally defined in the smartbinding set and can exist independently + * of the smartbinding object. + * Example: + * This example shows a fully specified smartbinding. Usually only parts + * are used. This example shows a tree with files and folders. + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * Remarks: + * The smartbinding parts can also be assigned to an element by adding them + * directly as a child in aml. + * + * + * + * ... + * + * + * + * + * + * See: + * There are several ways to be less verbose in assigning certain rules. + *
    + *
  • {@link baseclass.multiselectbinding.binding.each}
  • + *
  • {@link baseclass.dragdrop.attribute.drag}
  • + *
  • {@link element.bindings}
  • + *
  • {@link element.actions}
  • + *
  • {@link element.dragdrop}
  • + *
+ */ + this.$propHandlers["smartbinding"] = + + /** + * @attribute {String} actions the id of the actions element which + * provides the action rules for this element. Action rules are used to + * send changes on the bound data to a server. + * Example: + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * Undo + * + */ + this.$propHandlers["actions"] = + + /** + * @attribute {String} bindings the id of the bindings element which + * provides the binding rules for this element. + * Example: + * This example shows a set of binding rules that transform data into the + * representation of a list. In this case it displays the names of + * several email accounts, with after each account name the number of unread + * mails in that account. It uses JSLT to transform the caption. + * + * + * + * Account 1 + * + * + * + * + * Account 2 + * + * + * + * + * + * [text()] (#[mail[@read != 'true']]) + * + * + * + * + * Remarks: + * Bindings can also be assigned directly by putting the bindings tag as a + * child of this element. + * + * If the rule only contains a select attribute, it can be written in a + * short way by adding an attribute with the name of the rule to the + * element itself: + * + * + * + */ + this.$propHandlers["bindings"] = function(value, prop){ + var local = "$" + prop + "Element"; + if (this[local]) + this[local].unregister(this); + + if (!value) + return; + + + + apf.nameserver.get(prop, value).register(this); + + + if (prop != "actions" && + this.$checkLoadQueue() === false && this.$amlLoaded) + 1+1; //@todo add reload queue. + //this.reload(); + }; + + + var eachBinds = {"caption":1, "icon":1, "select":1, "css":1, "sort":1, + "drop":2, "drag":2, "dragcopy":2, "eachvalue":1}; //Similar to apf.Class + + this.$addAttrBind = function(prop, fParsed, expression) { + //Detect if it uses an external model + if (fParsed.models) { + + if (this.hasFeature(apf.__MULTISELECT__)) { + + } + + } + + //Set listener for all models + var i, xpath, modelId, model, + paths = fParsed.xpaths, + list = {}; + //@todo when there is no model in xpath modelId == null... + for (i = 0; i < paths.length; i+=2) { + if (!list[(modelId = paths[i])]) + list[modelId] = 1; + else list[modelId]++ + } + + if (!this.$propsUsingMainModel) + this.$propsUsingMainModel = {}; + + var rule = (this.$attrBindings || (this.$attrBindings = {}))[prop] = { + cvalue : fParsed, + value : expression, + compile : apf.BindingRule.prototype.$compile, + models : [] + }; + + delete this.$propsUsingMainModel[prop]; + for (xpath, i = 0; i < paths.length; i+=2) { + modelId = paths[i]; + if (list[modelId] == -1) + continue; + + xpath = paths[i + 1]; + + if (modelId == "#" || xpath == "#") { + var m = (rule.cvalue3 || (rule.cvalue3 = apf.lm.compile(rule.value, { + xpathmode: 5 + }))).call(this, this.xmlRoot); + + //@todo apf3 this needs to be fixed in live markup + if (typeof m != "string") { + model = m.model && m.model.$isModel && m.model; + if (model) + xpath = m.xpath; + else if (m.model) { + model = typeof m.model == "string" ? apf.xmldb.findModel(m.model) : m.model; + xpath = apf.xmlToXpath(m.model, model.data) + (m.xpath ? "/" + m.xpath : ""); //@todo make this better + } + else { + //wait until model becomes available + this.addEventListener("prop." + prop, function(e){ + var m = (rule.cvalue3 || (rule.cvalue3 = apf.lm.compile(rule.value, { + xpathmode: 5 + }))).call(this, this.xmlRoot); + + if (m.model) { + this.removeEventListener("prop." + prop, arguments.callee); + var _self = this; + $setTimeout(function(){ + _self.$clearDynamicProperty(prop); + _self.$setDynamicProperty(prop, expression); + }, 10); + } + }); + continue; + } + } + else model = null; + } + else model = null; + + if (!model) { + if (modelId) { + + //@todo apf3.0 how is this cleaned up??? + //Add change listener to the data of the model + model = apf.nameserver.get("model", modelId) //is model creation useful here? + || apf.setReference(modelId, apf.nameserver.register("model", modelId, new apf.model())); + + } + else { + if (!this.$model && !this.$initingModel) + initModel.call(this); + + model = this.$model; + + if (!this.hasFeature(apf.__MULTISELECT__) + && eachBinds[prop] != 2 || !eachBinds[prop]) //@experimental - should not set this because model will load these attributes + this.$propsUsingMainModel[prop] = { + xpath : xpath, + optimize : list[modelId] == 1 + }; + } + } + + //@todo warn here if no model?? + if (model && (!this.hasFeature(apf.__MULTISELECT__) + && eachBinds[prop] != 2 || !eachBinds[prop])) { + //Create the attribute binding + //@todo: remove listenRoot = expression.indexOf("*[") > -1 -> because it doesnt make sense in certain context. recheck selection handling + model.$bindXmlProperty(this, prop, xpath, list[modelId] == 1); + rule.models.push(model); + } + + list[modelId] = -1; + } + + rule.xpath = xpath; + + this.$canLoadDataAttr = eachBinds[prop] == 1; //@todo apf3.0 remove + this.$checkLoadQueue(); + } + + this.$removeAttrBind = function(prop){ + //@todo apf3.0 + //$model.$unbindXmlProperty + var rule = this.$attrBindings[prop] + if (!rule) + return; + + delete this.$attrBindings[prop]; + delete this.$propsUsingMainModel[prop] + + var models = rule.models; + if (models.length) + for (var i = 0; i < models.length; i++) { + models[i].$unbindXmlProperty(this, prop); + } + else if (this.$model) + this.$model.$unbindXmlProperty(this, prop); + }; + + this.$initingModel; + function initModel(){ + this.$initingModel = true; + this.$setInheritedAttribute("model"); + } + + this.addEventListener("DOMNodeInsertedIntoDocument", function(e){ + //Set empty message if there is no data + if (!this.model && this.$setClearMessage && !this.value) + this.$setClearMessage(this["empty-message"], "empty"); + + this.$amlLoaded = true; //@todo this can probably be removed + this.$checkLoadQueue(); + }); + + + + /** + * @attribute {String} model the name of the model to load data from or a + * datainstruction to load data. + * Example: + * + * + * + * + * + * + * + * + * + * Example: + * + * + * + * + * + * + * + * + * Example: + * + * + * + * + * + * + * + * + * + * Remarks: + * This attribute is inherited from a parent when not set. You can use this + * to tell sets of elements to use the same model. + * + * + * Name + * + * + * Happiness + * + * + * + * + * + * + * + * When no model is specified the default model is chosen. The default + * model is the first model that is found without a name, or if all models + * have a name, the first model found. + * Example: + * This example shows a dropdown from which the user can select a country. + * The list of countries is loaded from a model. Usually this would be loaded + * from a separate url, but for clarity it's inlined. When the user selects + * a country in the dropdown the value of the item is stored in the second + * model (mdlForm) at the position specified by the ref attribute. In this + * case this is the country element. + * + * Name + * + * + * Country + * + * + * + * + * + * USA + * Great Britain + * The Netherlands + * + * + * + * + * + * + * + * + * + * + * @see baseclass.databinding.attribute.model + */ + this.$propHandlers["model"] = function(value){ + //Unset model + if (!value && !this.$modelParsed) { + if (this.$model) { + this.clear(); + this.$model.unregister(this); + this.$model = null; + this.lastModelId = ""; + } + else if (this.dataParent) + this.dataParent.parent = null; //Should be autodisconnected by property binding + + return; + } + this.$initingModel = true; + + var fParsed; + //Special case for property binding + if ((fParsed = this.$modelParsed) && fParsed.type != 2) { + var found, pb = fParsed.props; + + if (this.dataParent) + this.dataParent = null; //Should be autodisconnected by property binding + + //Try to figure out who is the dataParent + for (var prop in pb){ + + + this.dataParent = { + parent : self[prop.split(".")[0]], + xpath : null, + model : this.$modelParsed.instruction + }; + + found = true; + break; // We currently only support one data parent + } + + if (found) { + //@todo this statement doesnt make sense + /*//Maybe a compound model is found + if (!this.dataParent && (pb = fParsed.xpaths && fParsed.xpaths[0])) { + this.dataParent = { + parent : self[pb.split(".")[0]], + xpath : fParsed.xpaths[1], + model : this.$modelParsed.instruction + }; + }*/ + + if (this.dataParent && !this.dataParent.signalXmlUpdate) + this.dataParent.signalXmlUpdate = {}; + } + + this.$modelParsed = null; + } + + //Analyze the data + var model; + if (typeof value == "object") { + if (value.dataType == apf.ARRAY) { //Optimization used for templating + + model = apf.nameserver.get("model", value[0]); + model.register(this, value[1]); + return; + + } + else if (value.$isModel) { // A model node is passed + //Convert model object to value; + model = value; + value = this.model = model.name; + if (!value) + model.setProperty("id", value = this.model = "model" + model.$uniqueId); + + //@todo why not set directly here? + } + else { //if (this.dataParent) { //Data came through data parent + if (this.dataParent) + this.model = this.dataParent.model; //reset this property + + model = apf.xmldb.findModel(value); + if (!model) //@todo very strange, this should never happen, but it does + return; + var xpath = apf.xmlToXpath(value, null, true) || "."; + + + + model.register(this, xpath); + return; + } + /*else { + //@todo Error ?? + }*/ + } + else if (value.indexOf("[::") > -1) { //@experimental + var model, pNode = this; + do { + pNode = pNode.parentNode + model = pNode.getAttribute("model"); + } + while (pNode.parentNode && pNode.parentNode.nodeType == 1 && (!model || model == value)); + + if (model && typeof model == "object") + model = model.id; + + this.$inheritProperties.model = 3; + if (model) { + value = value.replace(/\[\:\:/g, "[" + model + "::"); + } + else { + apf.console.warn("No found model on any of the parents for this element while trying to overload model: " + value); + return; + } + } + + //Optimize xmlroot position and set model async (unset the old one) + //@todo apf3.0 this could be optimized by using apf.queue and only when not all info is there... + clearTimeout(this.$dbTimer); + if (!this.$amlLoaded && this.nodeType == 1) { + var _self = this; + this.$dbTimer = $setTimeout(function(){ + if (!_self.$amlDestroyed) + apf.setModel(value, _self); + }); + } + else + apf.setModel(value, this); + }; + + + /** + * @attribute {String} viewport the way this element renders its data. + * Possible values: + * virtual this element only renders data that it needs to display. + * normal this element renders all data at startup. + * @experimental + */ + this.$propHandlers["viewport"] = function(value){ + if (value != "virtual") + return; + + this.implement(apf.VirtualViewport); + }; + +}; + + apf.DataBinding.prototype = new apf[apf.Presentation ? "Presentation" : "AmlElement"](); + + +apf.config.$inheritProperties["model"] = 1; +apf.config.$inheritProperties["empty-message"] = 1; +apf.config.$inheritProperties["loading-message"] = 1; +apf.config.$inheritProperties["offline-message"] = 1; +apf.config.$inheritProperties["noloading"] = 1; + +apf.Init.run("databinding"); + + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/core/baseclasses/databinding/multiselect.js)SIZE(47502)TIME(Tue, 13 Dec 2011 13:33:58 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + +/** + * All elements inheriting from this {@link term.baseclass baseclass} can bind to data + * which contains multiple nodes. + * + * @allowchild item, choices + * @define choices Container for item nodes which receive presentation. + * This element is part of the XForms specification. It is not necesary for + * the Ajax.org Markup Language. + * Example: + * + * + * + * red + * blue + * green + * + * + * + * @allowchild item + * + * @constructor + * @baseclass + * @default_private + */ +apf.MultiselectBinding = function(){ + if (!this.setQueryValue) + this.implement(apf.DataBinding); + + this.$regbase = this.$regbase|apf.__MULTISELECT__; //We're pretending to have multiselect even though we might not. + + this.$init(function(){ + this.$selectTimer = {}; + }); +}; + +(function(){ + this.length = 0; + + //1 = force no bind rule, 2 = force bind rule + this.$attrExcludePropBind = apf.extend({ + caption : 2, + icon : 2, + eachvalue : 2, + select : 2, + css : 2, + sort : 2, + drag : 2, + drop : 2, + dragcopy : 2, + selected : 3, + //caret : 2, + each : 1, + "selection" : 3, //only databound when has an xpath + "selection-unique" : 3, //only databound when has an xpath + "selection-constructor" : 3 //only databound when has an xpath + }, this.$attrExcludePropBind); + + + /** + * Change the sorting order of this element + * + * @param {Object} options the new sort options. These are applied incrementally. + * Any property not set is maintained unless the clear + * parameter is set to true. + * Properties: + * {String} order see {@link baseclass.multiselectbinding.binding.each.attribute.order} + * {String} [xpath] see {@link baseclass.multiselectbinding.binding.each.attribute.sort} + * {String} [type] see {@link baseclass.multiselectbinding.binding.each.attribute.data-type} + * {String} [method] see {@link baseclass.multiselectbinding.binding.each.attribute.sort-method} + * {Function} [getNodes] Function that retrieves a list of nodes. + * {String} [dateFormat] see {@link baseclass.multiselectbinding.binding.each.attribute.date-format} + * {Function} [getValue] Function that determines the string content based + * on an xml node as it's first argument. + * @param {Boolean} clear removes the current sort options. + * @param {Boolean} noReload whether to reload the data of this component. + * @see baseclass.multiselectbinding.binding.each + */ + this.resort = function(options, clear, noReload){ + if (!this.$sort) + this.$sort = new apf.Sort(); + + this.$sort.set(options, clear); + + if (this.clearAllCache) + this.clearAllCache(); + + if (noReload) + return; + + + /*if(this.hasFeature(apf.__VIRTUALVIEWPORT__)){ + this.$clearVirtualDataset(this.xmlRoot); + this.reload(); + + return; + }*/ + + + var _self = this; + (function sortNodes(xmlNode, htmlParent) { + if(!xmlNode) + return; + var sNodes = _self.$sort.apply( + apf.getArrayFromNodelist(xmlNode.selectNodes(_self.each))); + + for (var i = 0; i < sNodes.length; i++) { + if (_self.$isTreeArch || _self.$withContainer){ + var htmlNode = apf.xmldb.findHtmlNode(sNodes[i], _self); + + + + var container = _self.$findContainer(htmlNode); + + htmlParent.appendChild(htmlNode); + if (!apf.isChildOf(htmlNode, container, true)) + htmlParent.appendChild(container); + + sortNodes(sNodes[i], container); + } + else + htmlParent.appendChild(apf.xmldb.findHtmlNode(sNodes[i], _self)); + } + })(this.xmlRoot, this.$container); + + return options; + }; + + /** + * Change sorting from ascending to descending and vice versa. + */ + this.toggleSortOrder = function(){ + return this.resort({"ascending" : !this.$sort.get().ascending}).ascending; + }; + + /** + * Retrieves the current sort options + * + * @returns {Object} the current sort options. + * Properties: + * {String} order see {@link baseclass.multiselectbinding.binding.each.attribute.order} + * {String} xpath see {@link baseclass.multiselectbinding.binding.each.attribute.sort} + * {String} type see {@link baseclass.multiselectbinding.binding.each.attribute.data-type} + * {String} method see {@link baseclass.multiselectbinding.binding.each.attribute.sort-method} + * {Function} getNodes Function that retrieves a list of nodes. + * {String} dateFormat see {@link baseclass.multiselectbinding.binding.each.attribute.date-format} + * {Function} getValue Function that determines the string content based on + * an xml node as it's first argument. + * @see baseclass.multiselectbinding.binding.each + */ + this.getSortSettings = function(){ + return this.$sort.get(); + }; + + + /** + * Optimizes load time when the xml format is very simple. + */ + this.$propHandlers["simpledata"] = function(value){ + if (value) { + this.getTraverseNodes = function(xmlNode){ + return (xmlNode || this.xmlRoot).childNodes; + }; + + this.getFirstTraverseNode = function(xmlNode){ + return (xmlNode || this.xmlRoot).childNodes[0]; + }; + + this.getLastTraverseNode = function(xmlNode){ + var nodes = (xmlNode || this.xmlRoot).childNodes; + return nodes[nodes.length - 1]; + }; + + this.getTraverseParent = function(xmlNode){ + if (!xmlNode.parentNode || xmlNode == this.xmlRoot) + return false; + + return xmlNode.parentNode; + }; + } + else { + delete this.getTraverseNodes; + delete this.getFirstTraverseNode; + delete this.getLastTraverseNode; + delete this.getTraverseParent; + } + } + + /** + * Retrieves a nodelist containing the {@link term.datanode data nodes} which + * are rendered by this element (see each nodes, see + * {@link baseclass.multiselectbinding.binding.each}). + * + * @param {XMLElement} [xmlNode] the parent element on which the each query is applied. + */ + this.getTraverseNodes = function(xmlNode){ + + + + if (this.$sort) { + var nodes = apf.getArrayFromNodelist((xmlNode || this.xmlRoot).selectNodes(this.each)); + return this.$sort.apply(nodes); + } + + + return (xmlNode || this.xmlRoot).selectNodes(this.each); + }; + + /** + * Retrieves the first {@link term.datanode data node} which gets representation + * in this element + * (see each nodes, see {@link baseclass.multiselectbinding.binding.each}). + * + * @param {XMLElement} [xmlNode] the parent element on which the each query is executed. + * @return {XMLElement} + */ + this.getFirstTraverseNode = function(xmlNode){ + + if (this.$sort) { + var nodes = (xmlNode || this.xmlRoot).selectNodes(this.each); + return this.$sort.apply(nodes)[0]; + } + + + return (xmlNode || this.xmlRoot).selectSingleNode(this.each); + }; + + /** + * Retrieves the last {@link term.datanode data node} which gets representation + * in this element + * (see each nodes, see {@link baseclass.multiselectbinding.binding.each}). + * + * @param {XMLElement} [xmlNode] the parent element on which the each query is executed. + * @return {XMLElement} the last {@link term.datanode data node} + * @see baseclass.multiselectbinding.binding.each + */ + this.getLastTraverseNode = function(xmlNode){ + var nodes = this.getTraverseNodes(xmlNode || this.xmlRoot); + return nodes[nodes.length-1]; + }; + + /** + * Determines whether an {@link term.datanode data node} is a each node (see + * {@link baseclass.multiselectbinding.binding.each}) + * + * @param {XMLElement} [xmlNode] the parent element on which the each query is executed. + * @return {Boolean} whether the xml element is a each node. + * @see baseclass.multiselectbinding.binding.each + */ + this.isTraverseNode = function(xmlNode){ + /* + Added optimization, only when an object has a tree architecture is it + important to go up to the each parent of the xmlNode, else the node + should always be based on the xmlroot of this component + */ + //this.$isTreeArch + var nodes = this.getTraverseNodes( + this.getTraverseParent(xmlNode) || this.xmlRoot); + for (var i = 0; i < nodes.length; i++) + if (nodes[i] == xmlNode) + return true; + return false; + }; + + /** + * Retrieves the next each node (see {@link baseclass.multiselectbinding.binding.each}) + * to be selected + * from a given each node. The method can do this in either direction and + * also return the Nth node for this algorithm. + * + * @param {XMLElement} xmlNode the starting point for determining the next selection. + * @param {Boolean} [up] the direction of the selection. Default is false. + * @param {Integer} [count] the distance in number of nodes. Default is 1. + * @return {XMLElement} the {@link term.datanode data node} to be selected next. + * @see baseclass.multiselectbinding.binding.each + */ + this.getNextTraverseSelected = function(xmlNode, up, count){ + if (!xmlNode) + xmlNode = this.selected; + if (!count) + count = 1; + + var i = 0; + var nodes = this.getTraverseNodes(this.getTraverseParent(xmlNode) || this.xmlRoot); + while (nodes[i] && nodes[i] != xmlNode) + i++; + + var node = (up == null) + ? nodes[i + count] || nodes[i - count] + : (up ? nodes[i + count] : nodes[i - count]); + + //arguments[2] + return node || count && (i < count || (i + 1) > Math.floor(nodes.length / count) * count) + ? node + : (up ? nodes[nodes.length-1] : nodes[0]); + }; + + /** + * Retrieves the next each node (see {@link baseclass.multiselectbinding.binding.each}). + * The method can do this in either direction and also return the Nth next node. + * + * @param {XMLElement} xmlNode the starting point for determining the next node. + * @param {Boolean} [up] the direction. Default is false. + * @param {Integer} [count] the distance in number of nodes. Default is 1. + * @return {XMLElement} the next each node + * @see baseclass.multiselectbinding.binding.each + */ + this.getNextTraverse = function(xmlNode, up, count){ + if (!count) + count = 1; + if (!xmlNode) + xmlNode = this.selected; + + var i = 0; + var nodes = this.getTraverseNodes(this.getTraverseParent(xmlNode) || this.xmlRoot); + while (nodes[i] && nodes[i] != xmlNode) + i++; + + var ind = i + (up ? -1 * count : count); + return nodes[ind < 0 ? 0 : ind]; + }; + + /** + * Retrieves the parent each node (see {@link baseclass.multiselectbinding.binding.each}). + * In some cases the each rules has a complex form like 'children/item'. In + * those cases the generated tree has a different structure from that of the xml + * data. For these situations the xmlNode.parentNode property won't return + * the each parent, this method will give you the right parent. + * + * @param {XMLElement} xmlNode the node for which the parent element will be determined. + * @return {XMLElement} the parent node or null if none was found. + * @see baseclass.multiselectbinding.binding.each + */ + this.getTraverseParent = function(xmlNode){ + if (!xmlNode.parentNode || xmlNode == this.xmlRoot) + return false; + + //@todo this can be removed when we have a new xpath implementation + if (xmlNode.$regbase) + return xmlNode.parentNode; + + var x, id = xmlNode.getAttribute(apf.xmldb.xmlIdTag); + if (!id) { + //return false; + xmlNode.setAttribute(apf.xmldb.xmlIdTag, "temp"); + id = "temp"; + } + + /* + do { + xmlNode = xmlNode.parentNode; + if (xmlNode == this.xmlRoot) + return false; + if (this.isTraverseNode(xmlNode)) + return xmlNode; + } while (xmlNode.parentNode); + */ + + //This is not 100% correct, but good enough for now + + x = xmlNode.selectSingleNode("ancestor::node()[((" + + this.each + ")/@" + apf.xmldb.xmlIdTag + ")='" + + id + "']"); + + if (id == "temp") + xmlNode.removeAttribute(apf.xmldb.xmlIdTag); + return x; + }; + + /** + * Finds HTML presentation node in cache by ID + * + * @param {String} id the id of the HTMLElement which is looked up. + * @return {HTMLElement} the HTMLElement found. When no element is found, null is returned. + */ + if (!this.$findHtmlNode) { //overwritten by apf.Cache + this.$findHtmlNode = function(id){ + return this.$pHtmlDoc.getElementById(id); + }; + } + + this.$setClearMessage = function(msg, className, lastHeight){ + if (this.more && this.$addMoreItem) this.$addMoreItem(); + if (!this.$empty) { + if (!this.$hasLayoutNode("empty")) + return; + + this.$getNewContext("empty"); + + var xmlEmpty = this.$getLayoutNode("empty"); + if (!xmlEmpty) return; + + this.$empty = apf.insertHtmlNode(xmlEmpty, this.$container); + } + else { + this.$container.appendChild(this.$empty); + } + + var empty = this.$getLayoutNode("empty", "caption", this.$empty); + + if (empty) + apf.setNodeValue(empty, msg || ""); + + this.$empty.setAttribute("id", "empty" + this.$uniqueId); + apf.setStyleClass(this.$empty, className, ["loading", "empty", "offline"]); + + //@todo apf3.0 cleanup? + var extH = apf.getStyle(this.$ext, "height"); + this.$empty.style.height = (lastHeight && (!extH || extH == "auto") && className != "empty") + ? (Math.max(10, (lastHeight + - apf.getHeightDiff(this.$empty) + - apf.getHeightDiff(this.$ext))) + "px") + : ""; + }; + + this.$updateClearMessage = function(msg, className) { + if (!this.$empty || this.$empty.parentNode != this.$container + || this.$empty.className.indexOf(className) == -1) + return; + + var empty = this.$getLayoutNode("empty", "caption", this.$empty); + if (empty) + apf.setNodeValue(empty, msg || ""); + } + + this.$removeClearMessage = function(){ + if (!this.$empty) + this.$empty = document.getElementById("empty" + this.$uniqueId); + if (this.$empty && this.$empty.parentNode) + this.$empty.parentNode.removeChild(this.$empty); + }; + + /** + * Set listeners, calls HTML creation methods and + * initializes select and focus states of object. + */ + this.$load = function(XMLRoot){ + //Add listener to XMLRoot Node + apf.xmldb.addNodeListener(XMLRoot, this); + + var length = this.getTraverseNodes(XMLRoot).length; + if (!this.renderRoot && !length) + return this.clear(null, null, true); //@todo apf3.0 this should clear and set a listener + + //Traverse through XMLTree + var nodes = this.$addNodes(XMLRoot, null, null, this.renderRoot, null, 0, "load"); + + //Build HTML + this.$fill(nodes); + + //Select First Child + if (this.selectable) { + + //@todo apf3.0 optimize to not set selection when .selection or .selected is set on initial load + if (this["default"]) + this.select(this["default"]); + else if (this.autoselect) { + if (!this.selected) { + if (this.renderRoot) + this.select(XMLRoot, null, null, null, true); + else if (nodes.length) + this.$selectDefault(XMLRoot); + //else @todo apf3.0 this one doesnt seem needed + //this.clearSelection(); + } + } + else { + this.clearSelection(true); + var xmlNode = this.renderRoot + ? this.xmlRoot + : this.getFirstTraverseNode(); //should this be moved to the clearSelection function? + if (xmlNode) + this.setCaret(xmlNode); + + if (this.selected) + this.setProperty("selected", null); + if (this.choosen) + this.setProperty("choosen", null); + + } + } + + if (this.focussable) + apf.document.activeElement == this ? this.$focus() : this.$blur(); + + + if (length != this.length) + this.setProperty("length", length); + + }; + + var actionFeature = { + "insert" : 127,//11111110 + "replacenode" : 127,//11111110 + "attribute" : 255,//11111111 + "add" : 251,//11110111 + "remove" : 110, //01011110 + "redo-remove" : 79, //10011110 + "synchronize" : 127,//11111110 + "move-away" : 297,//11010111 + "move" : 141 //10011111 + }; + + /** + * Loops through parents of changed node to find the first + * connected node. Based on the action it will change, remove + * or update the representation of the data. + * + * @event xmlupdate Fires when xml of this element is updated. + * object: + * {String} action the action that was executed on the xml. + * Possible values: + * text a text node is set. + * attribute an attribute is set. + * update an xml node is updated. + * insert xml nodes are inserted. + * add an xml node is added. + * remove an xml node is removed (parent still set). + * redo-remove an xml node is removed (parent not set). + * synchronize unknown update. + * move-away an xml node is moved (parent not set). + * move an xml node is moved (parent still set). + * {XMLElement} xmlNode the node that is subject to the update. + * {Mixed} result the result. + * {UndoObj} UndoObj the undo information. + */ + this.$xmlUpdate = function(action, xmlNode, listenNode, UndoObj, lastParent){ + if (!this.xmlRoot) + return; //@todo think about purging cache when xmlroot is removed + + var result, length, pNode, htmlNode, + startNode = xmlNode; + if (!listenNode) + listenNode = this.xmlRoot; + + if (action == "redo-remove") { + var loc = [xmlNode.parentNode, xmlNode.nextSibling]; + lastParent.appendChild(xmlNode); //ahum, i'm not proud of this one + var eachNode = this.isTraverseNode(xmlNode); + if (loc[0]) + loc[0].insertBefore(xmlNode, loc[1]); + else + lastParent.removeChild(xmlNode); + + if (!eachNode) + xmlNode = lastParent; + } + + //Get First ParentNode connected + do { + if (action == "add" && this.isTraverseNode(xmlNode) + && startNode == xmlNode) + break; //@todo Might want to comment this out for adding nodes under a eachd node + + if (xmlNode.getAttribute(apf.xmldb.xmlIdTag)) { + htmlNode = this.$findHtmlNode( + xmlNode.getAttribute(apf.xmldb.xmlIdTag) + + "|" + this.$uniqueId); + + if (xmlNode == listenNode && !this.renderRoot) { + if (xmlNode == this.xmlRoot && action != "insert" && action != "replacenode") { + //@todo apf3.0 - fix this for binding on properties + this.dispatchEvent("xmlupdate", { + action : action, + xmlNode: xmlNode, + UndoObj: UndoObj + }); + return; + } + break; + } + + if (htmlNode && actionFeature[action] & 2 + && !this.isTraverseNode(xmlNode)) + action = "remove"; //@todo why not break here? + + else if (!htmlNode && actionFeature[action] & 4 + && this.isTraverseNode(xmlNode)){ + action = "add"; + break; + } + + else if (htmlNode + && (startNode != xmlNode || xmlNode == this.xmlRoot)) { + if (actionFeature[action] & 1) + action = "update"; + else if (action == "remove") + return; + } + + if (htmlNode || action == "move") + break; + } + else if (actionFeature[action] & 8 && this.isTraverseNode(xmlNode)){ + action = "add"; + break; + } + + if (xmlNode == listenNode) { + if (actionFeature[action] & 128) //The change is not for us. + return; + + break; + } + xmlNode = xmlNode.parentNode; + } + while (xmlNode && xmlNode.nodeType != 9); + + + + + /** + * @todo Think about not having this code here + */ + if (this.hasFeature(apf.__VIRTUALVIEWPORT__)) { + if(!this.$isInViewport(xmlNode)) //xmlNode is a eachd node + return; + } + + + //if(xmlNode == listenNode && !action.match(/add|synchronize|insert/)) + // return; //deleting nodes in parentData of object + + var foundNode = xmlNode; + if (xmlNode && xmlNode.nodeType == 9) + xmlNode = startNode; + + if (action == "replacenode") { + //var tmpNode; + //Case for replacing the xmlroot or its direct parent + if (UndoObj ? UndoObj.args[1] == this.xmlRoot : !this.xmlRoot.parentNode) + return this.load(UndoObj ? UndoObj.xmlNode : listenNode, {force: true}); + + //Case for replacing a node between the xmlroot and the traverse nodes + var nodes = this.getTraverseNodes(); + for (var i = 0, l = nodes.length; i < l; i++) { + if (apf.isChildOf(startNode, nodes[i])) + return this.load(this.xmlRoot, {force: true}); //This can be more optimized by using addNodes + } + //if ((tmpNode = this.getFirstTraverseNode()) && apf.isChildOf(startNode, tmpNode)) + } + + //Action Tracker Support - && xmlNode correct here??? - UndoObj.xmlNode works but fishy.... + if (UndoObj && xmlNode && !UndoObj.xmlNode) + UndoObj.xmlNode = xmlNode; + + //Check Move -- if value node isn't the node that was moved then only perform a normal update + if (action == "move" && foundNode == startNode) { + //if(!htmlNode) alert(xmlNode.getAttribute("id")+"|"+this.$uniqueId); + var isInThis = apf.isChildOf(this.xmlRoot, xmlNode.parentNode, true); //@todo this.getTraverseParent(xmlNode) + var wasInThis = apf.isChildOf(this.xmlRoot, UndoObj.extra.parent, true); + + //Move if both previous and current position is within this object + if (isInThis && wasInThis) + this.$moveNode(xmlNode, htmlNode, UndoObj.extra.oldParent); + else if (isInThis) //Add if only current position is within this object + action = "add"; + else if (wasInThis) //Remove if only previous position is within this object + action = "remove"; + } + else if (action == "move-away") { + var goesToThis = apf.isChildOf(this.xmlRoot, UndoObj.extra.parent, true); + if (!goesToThis) + action = "remove"; + } + + //Remove loading message + if (this.$removeClearMessage && this.$setClearMessage) { + if (this.getFirstTraverseNode()) + this.$removeClearMessage(); + else + this.$setClearMessage(this["empty-message"], "empty") + } + + //Check Insert + if (action == "insert" && (this.$isTreeArch || xmlNode == this.xmlRoot)) { + if (!xmlNode) + return; + + if (this.$hasLoadStatus(xmlNode) && this.$removeLoading) + this.$removeLoading(xmlNode); + + if (this.$container.firstChild && !apf.xmldb.getNode(this.$container.firstChild)) { + //Appearantly the content was cleared + this.$container.innerHTML = ""; + + if (!this.renderRoot) { + length = this.getTraverseNodes().length; + if (!length) + this.clear(); + } + } + + result = this.$addNodes(xmlNode, null, true, false, null, null, "insert");//this.$isTreeArch?? + + this.$fillParentHtml = (this.$getParentNode + ? this.$getParentNode(htmlNode) + : htmlNode); + this.$fillParent = xmlNode; + this.$fill(result); + + + + if (this.selectable && (length === 0 || !this.xmlRoot.selectSingleNode(this.each))) + return; + } + else if (action == "add") {// || !htmlNode (Check Add) + var parentHTMLNode; + pNode = this.getTraverseParent(xmlNode); + + if (pNode == this.xmlRoot) + parentHTMLNode = this.$container; + + if (!parentHTMLNode && this.$isTreeArch) { + parentHTMLNode = this.$findHtmlNode( + pNode.getAttribute(apf.xmldb.xmlIdTag) + "|" + this.$uniqueId); + } + + //This should be moved into a function (used in setCache as well) + + if (!parentHTMLNode && this.getCacheItem) + parentHTMLNode = this.getCacheItem(pNode.getAttribute(apf.xmldb.xmlIdTag) + || (pNode.getAttribute(apf.xmldb.xmlDocTag) + ? "doc" + pNode.getAttribute(apf.xmldb.xmlDocTag) + : false)); + + + //Only update if node is in current representation or in cache + if (parentHTMLNode || this.$isTreeArch + && pNode == this.xmlRoot) { //apf.isChildOf(this.xmlRoot, xmlNode) + parentHTMLNode = (this.$findContainer && parentHTMLNode + ? this.$findContainer(parentHTMLNode) + : parentHTMLNode) || this.$container; + + result = this.$addNodes(xmlNode, parentHTMLNode, true, true, + apf.xmldb.getHtmlNode(this.getNextTraverse(xmlNode), this)); + + if (parentHTMLNode) + this.$fill(result); + } + } + else if (action == "remove") { //Check Remove + //&& (!xmlNode || foundNode == xmlNode && xmlNode.parentNode + //if (!xmlNode || startNode != xmlNode) //@todo unsure if I can remove above commented out statement + //return; + //I've commented above code out, because it disabled removing a + //subnode of a node that through an each rule makes the traverse + //node no longer a traverse node. + + //Remove HTML Node + if (htmlNode) + this.$deInitNode(xmlNode, htmlNode); + else if (startNode == this.xmlRoot) { + return this.load(null, { + noClearMsg: !this.dataParent || !this.dataParent.autoselect + }); + } + } + else if (htmlNode) { + + if (this.$sort) + this.$moveNode(xmlNode, htmlNode); + + + this.$updateNode(xmlNode, htmlNode); + + //Transaction 'niceties' + if (action == "replacenode" && this.hasFeature(apf.__MULTISELECT__) + && this.selected && xmlNode.getAttribute(apf.xmldb.xmlIdTag) + == this.selected.getAttribute(apf.xmldb.xmlIdTag)) { + this.selected = xmlNode; + } + + //if(action == "synchronize" && this.autoselect) this.reselect(); + } + else if (action == "redo-remove") { //Check Remove of the data (some ancestor) that this component is bound on + var testNode = this.xmlRoot; + while (testNode && testNode.nodeType != 9) + testNode = testNode.parentNode; + + if (!testNode) { + //Set Component in listening state until data becomes available again. + var model = this.getModel(true); + + + + return model.$waitForXml(this); + } + } + + + + //For tree based nodes, update all the nodes up + pNode = xmlNode ? xmlNode.parentNode : lastParent; + if (this.$isTreeArch && !this.$preventRecursiveUpdate + && pNode && pNode.nodeType == 1) { + do { + htmlNode = this.$findHtmlNode(pNode.getAttribute( + apf.xmldb.xmlIdTag) + "|" + this.$uniqueId); + + if (htmlNode) + this.$updateNode(pNode, htmlNode); + } + while ((pNode = this.getTraverseParent(pNode)) && pNode.nodeType == 1); + } + + //Make sure the selection doesn't become corrupted + if (actionFeature[action] & 32 && this.selectable + && startNode == xmlNode + && (action != "insert" || xmlNode == this.xmlRoot)) { + + clearTimeout(this.$selectTimer.timer); + // Determine next selection + if (action == "remove" && apf.isChildOf(xmlNode, this.selected, true) + || xmlNode == this.$selectTimer.nextNode) { + this.$selectTimer.nextNode = this.getDefaultNext(xmlNode, this.$isTreeArch); + if (this.$selectTimer.nextNode == this.xmlRoot && !this.renderRoot) + this.$selectTimer.nextNode = null; + } + + //@todo Fix this by putting it after xmlUpdate when its using a timer + var _self = this; + this.$selectTimer.timer = $setTimeout(function(){ + _self.$checkSelection(_self.$selectTimer.nextNode); + _self.$selectTimer.nextNode = null; + }); + } + + + //Set dynamic properties that relate to the changed content + if (actionFeature[action] & 64) { + if (!length) + length = this.xmlRoot.selectNodes(this.each).length; + if (action == "remove") + length--; + if (length != this.length) + this.setProperty("length", length); + } + + + //Let's signal components that are waiting for xml to appear (@todo what about clearing the signalXmlUpdate) + if (this.signalXmlUpdate && actionFeature[action] & 16) { + var uniqueId; + for (uniqueId in this.signalXmlUpdate) { + if (parseInt(uniqueId) != uniqueId) continue; //safari_old stuff + + var o = apf.lookup(uniqueId); + if (!this.selected) continue; + + xmlNode = this.selected.selectSingleNode(o.dataParent.xpath); + if (!xmlNode) continue; + + o.load(xmlNode); + } + } + + this.dispatchEvent("xmlupdate", { + action : action, + xmlNode: startNode, + traverseNode : xmlNode, + result : result, + UndoObj: UndoObj + }); + }; + + /** + * Loop through NodeList of selected Traverse Nodes + * and check if it has representation. If it doesn't + * representation is created via $add(). + */ + this.$addNodes = function(xmlNode, parent, checkChildren, isChild, insertBefore, depth, action){ + + + var htmlNode, lastNode; + isChild = (isChild && (this.renderRoot && xmlNode == this.xmlRoot + || this.isTraverseNode(xmlNode))); + var nodes = isChild ? [xmlNode] : this.getTraverseNodes(xmlNode); + /*var loadChildren = nodes.length && this.$bindings["insert"] + ? this.$applyBindRule("insert", xmlNode) + : false; << UNUSED */ + + + var cId, cItem; + if (this.$isTreeArch && this.caching + && (!this.$bindings || !this.$bindings.each || !this.$bindings.each.filter) + && (cItem = this.cache[(cId = xmlNode.getAttribute(apf.xmldb.xmlIdTag))])) { + if (this.$subTreeCacheContext || this.$needsDepth) { + //@todo + //We destroy the current items, because currently we + //don't support multiple treecachecontexts + //and because datagrid needs to redraw depth + this.clearCacheItem(cId); + } + else { + this.$subTreeCacheContext = { + oHtml : cItem, + container : parent, + parentNode : null, + beforeNode : null + }; + + var htmlNode; + while (cItem.childNodes.length) + (parent || this.$container).appendChild(htmlNode = cItem.childNodes[0]); + + return nodes; + } + } + + + if (this.$isTreeArch && depth === null && action == "insert") { + depth = 0, loopNode = xmlNode; + while(loopNode && loopNode != this.xmlRoot) { + depth++; + loopNode = this.getTraverseParent(loopNode); + } + } + + for (var i = 0; i < nodes.length; i++) { + if (nodes[i].nodeType != 1) { + + continue; + } + + if (checkChildren) { + htmlNode = this.$findHtmlNode(nodes[i] + .getAttribute(apf.xmldb.xmlIdTag) + "|" + this.$uniqueId); + } + + if (!htmlNode) { + //Retrieve DataBind ID + var Lid = apf.xmldb.nodeConnect(this.documentId, nodes[i], null, this); + + //Add Children + var beforeNode = isChild + ? insertBefore + : (lastNode ? lastNode.nextSibling : null),//(parent || this.$container).firstChild); + parentNode = this.$add(nodes[i], Lid, isChild ? xmlNode.parentNode : xmlNode, + beforeNode ? parent || this.$container : parent, beforeNode, + (!beforeNode && i == nodes.length - 1), depth, nodes[i + 1], action);//Should use getTraverParent + + //Exit if component tells us its done with rendering + if (parentNode === false) { + //Tag all needed xmlNodes for future reference + // @todo apf3.0 code below looks harmful... hence commented out (Mike) + /*for (var j = i; j < nodes.length; j++) + apf.xmldb.nodeConnect(this.documentId, nodes[j], + null, this);*/ + break; + } + + //Parse Children Recursively -> optimize: don't check children that can't exist + //if(this.$isTreeArch) this.$addNodes(nodes[i], parentNode, checkChildren); + } + + if (checkChildren) + lastNode = htmlNode;// ? htmlNode.parentNode.parentNode : null; + } + + return nodes; + }; + + this.$handleBindingRule = function(value, prop){ + if (!value) + this[prop] = null; + + //@todo apf3.0 fix parsing + if (prop == "each") { + value = value.charAt(0) == "[" && value.charAt(value.length - 1) == "]" + ? value.replace(/^\[|\]$/g, "") + : value; + + if (value.match(/^\w+::/)) { + var model = value.split("::"); //@todo this is all very bad + if (!apf.xPathAxis[model[0]]) { + this.setProperty("model", model[0]); + this.each = model[1]; + } + else + this.each = value; + } + else + this.each = value; + + if (this.each == this.$lastEach) + return; + + this.$lastEach = value; + + if (!this.$model && !this.$initingModel) { + this.$initingModel = true; + this.$setInheritedAttribute("model"); + + return; //@experimental + } + + if (this.$checkLoadQueue() !== false) //@experimental + return; + } + + //@todo apf3.0 find a better heuristic (portal demo) + if (this.xmlRoot && !this.$bindRuleTimer && this.$amlLoaded) { + var _self = this; + apf.queue.add("reload" + this.$uniqueId, function(){ + + _self.reload(); + }); + } + }; + + this.$select = function(o){ + + if (this.renaming) + this.stopRename(null, true); + + + if (!o || !o.style) + return; + return this.$setStyleClass(o, "selected"); + }; + + this.$deselect = function(o){ + + if (this.renaming) { + this.stopRename(null, true); + + if (this.ctrlselect) + return false; + } + + + if (!o) + return; + return this.$setStyleClass(o, "", ["selected", "indicate"]); + }; + + this.$indicate = function(o){ + + if (this.renaming) + this.stopRename(null, true); + + + if (!o) + return; + return this.$setStyleClass(o, "indicate"); + }; + + this.$deindicate = function(o){ + + if (this.renaming) + this.stopRename(null, true); + + + if (!o) + return; + return this.$setStyleClass(o, "", ["indicate"]); + }; + + + /** + * @attribute {String} each the xpath statement that determines which + * {@link term.datanode data nodes} are rendered by this element (also known + * as {@link term.eachnode each nodes}. See + * {@link baseclass.multiselectbinding.binding.each} for more information. + * Example: + * + * Country + * + * + * + * + * + * USA + * Great Brittain + * The Netherlands + * ... + * + * + * + * @see baseclass.multiselectbinding.binding.each + */ + this.$propHandlers["each"] = + + /** + * @attribute {String} caption the xpath statement that determines from + * which xml node the caption is retrieved. + * Example: + * + * + * + */ + this.$propHandlers["caption"] = + + /** + * @attribute {String} valuerule the xpath statement that determines from + * which xml node the value is retrieved. + * Example: + * + * + * + * @see baseclass.multiselect.binding.value + */ + this.$propHandlers["eachvalue"] = + + /** + * @attribute {String} icon the xpath statement that determines from + * which xml node the icon url is retrieved. + * Example: + * + * + * + */ + this.$propHandlers["icon"] = + + /** + * @attribute {String} tooltip the xpath statement that determines from + * which xml node the tooltip text is retrieved. + * Example: + * + * + * + */ + this.$propHandlers["tooltip"] = this.$handleBindingRule; + + + /** + * @attribute {String} sort the xpath statement that selects the sortable value. + * Example: + * + * + * + * @see element.each.attribute.sort + */ + this.$propHandlers["sort"] = function(value){ + if (value) { + this.$sort = new apf.Sort() + this.$sort.set({ + getValue : apf.lm.compile(value) + }); + } + else { + this.$sort = null; + } + } + + + /** + * @attribute {String} select the xpath statement that determines whether + * this node is selectable. + * Example: + * + * + * + * @see baseclass.multiselect.binding.select + */ + //this.$propHandlers["select"] = + +}).call(apf.MultiselectBinding.prototype = new apf.DataBinding()); + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/core/baseclasses/databinding/standard.js)SIZE(6499)TIME(Fri, 14 Oct 2011 08:01:44 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + + +/** + * @constructor + * @private + * @baseclass + */ +apf.StandardBinding = function(){ + this.$init(true); + + + if (apf.Validation) + this.implement(apf.Validation); + + + if (!this.setQueryValue) + this.implement(apf.DataBinding); + + if (!this.defaultValue) //@todo please use this in a sentence + this.defaultValue = ""; + + /** + * Load XML into this element + * @private + */ + this.$load = function(xmlNode){ + //Add listener to XMLRoot Node + apf.xmldb.addNodeListener(xmlNode, this); + //Set Properties + + + var b, lrule, rule, bRules, bRule, value; + if (b = this.$bindings) { + for (rule in b) { + lrule = rule.toLowerCase(); + if (this.$supportedProperties.indexOf(lrule) > -1) { + bRule = (bRules = b[lrule]).length == 1 + ? bRules[0] + : this.$getBindRule(lrule, xmlNode); + + value = bRule.value || bRule.match; + + + //Remove any bounds if relevant + this.$clearDynamicProperty(lrule); + + if (value.indexOf("{") > -1 || value.indexOf("[") > -1) + this.$setDynamicProperty(lrule, value); + else + + if (this.setProperty) + this.setProperty(lrule, value, true); + } + } + } + + + //Think should be set in the event by the Validation Class + if (this.errBox && this.isValid && this.isValid()) + this.clearError(); + }; + + /** + * Set xml based properties of this element + * @private + */ + this.$xmlUpdate = function(action, xmlNode, listenNode, UndoObj){ + //Clear this component if some ancestor has been detached + if (action == "redo-remove") { + var retreatToListenMode = false, model = this.getModel(true); + if (model) { + var xpath = model.getXpathByAmlNode(this); + if (xpath) { + xmlNode = model.data.selectSingleNode(xpath); + if (xmlNode != this.xmlRoot) + retreatToListenMode = true; + } + } + + if (retreatToListenMode || this.xmlRoot == xmlNode) { + + + //Set Component in listening state untill data becomes available again. + return model.$waitForXml(this); + } + } + + //Action Tracker Support + if (UndoObj && !UndoObj.xmlNode) + UndoObj.xmlNode = this.xmlRoot; + + //Set Properties + + + var b, lrule, rule, bRules, bRule, value; + if (b = this.$bindings) { + for (rule in b) { + lrule = rule.toLowerCase(); + if (this.$supportedProperties.indexOf(lrule) > -1) { + bRule = (bRules = b[lrule]).length == 1 + ? bRules[0] + : this.$getBindRule(lrule, xmlNode); + + value = bRule.value || bRule.match; + + + //Remove any bounds if relevant + this.$clearDynamicProperty(lrule); + + if (value.indexOf("{") > -1 || value.indexOf("[") > -1) + this.$setDynamicProperty(lrule, value); + else + + if (this.setProperty) + this.setProperty(lrule, value); + } + } + } + + + //@todo Think should be set in the event by the Validation Class + if (this.errBox && this.isValid && this.isValid()) + this.clearError(); + + this.dispatchEvent("xmlupdate", { + action : action, + xmlNode: xmlNode, + UndoObj: UndoObj + }); + }; + + /** + * Clears the data loaded into this element resetting it's value. + */ + //@todo apf3.0 this is wrong + this.addEventListener("$clear", function(nomsg, do_event){ + if (this.$propHandlers && this.$propHandlers["value"]) { + this.value = -99999; //force resetting + this.$propHandlers["value"].call(this, ""); + } + }); +}; +apf.StandardBinding.prototype = new apf.DataBinding(); + +apf.Init.run("standardbinding"); + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/core/baseclasses/multiselect.js)SIZE(71734)TIME(Wed, 30 Nov 2011 17:29:20 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + +apf.__MULTISELECT__ = 1 << 8; + + + +/** + * @term eachnode A each node is a {@link term.datanode data node} that is in the set selected by the + * {@link baseclass.multiselectbinding.binding.each each binding rule}. + * These {@link term.datanode data nodes} get representation within the visual element. For instance + * each item in a list is connected to such a each node. A each node + * can be selected, removed, added, dragged, dropped and so on. + * Example: + * In this example the person nodes that have the show attribute set to 1 are the + * each nodes of the list. This list will display three items. + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * Remarks: + * A somewhat advanced topic is understanding how an element can use the + * each {@link term.binding binding rule}. For the tree this binding rules + * can be used to create a virtual tree mapping of the xml. + */ + +/** + * @term caret When selecting nodes in a list using the keyboard, the caret is + * the indication of the position within that list. The item that the caret is + * on might or might not be selected. This feature is especially useful when + * holding the control key or using the shift key to multi select items. + */ + +/** + * All elements inheriting from this {@link term.baseclass baseclass} have selection features. This includes handling + * for multiselect and several keyboard based selection interaction. It also + * takes care of {@link term.caret caret} handling when multiselect is enabled. Furthermore features + * for dealing with multinode component are included like adding and removing + * {@link term.datanode data nodes}. + * + * @constructor + * @baseclass + * @author Ruben Daniels (ruben AT ajax DOT org) + * @version %I%, %G% + * @since 0.5 + * + * @inherits apf.MultiselectBinding + * + * @binding select Determines whether the {@link term.eachnode each node} can be selected. + * Example: + * In this example the tree contains nodes that have a disabled flag set. + * These nodes cannot be selected + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * @binding value Determines the way the value for the element is retrieved + * from the selected node. The value property contains this value. + * Example: + * + * + * + * + * + * + * + * + * + * red + * green + * blue + * + * + * + * + */ +apf.MultiSelect = function(){ + this.$init(function(){ + this.$valueList = []; + this.$selectedList = []; + }); +}; + +//@todo investigate if selectedList can be deprecated +(function() { + this.$regbase = this.$regbase|apf.__MULTISELECT__; + + /**** Properties ****/ + + /** + * the last selected item of this element. + * @type {XMLElement} + */ + this.sellength = 0; + this.selected = null; + this.$selected = null; + + /** + * the xml element that has the {@link term.caret caret}. + * @type {XMLElement} + */ + this.caret = null; + this.$caret = null; + + /** + * whether to use a {@link term.caret caret} in the interaction of this element. + * @type {Boolean} + */ + this.useindicator = true; + + + + /** + * Removes an {@link term.datanode data node} from the data of this element. + * Example: + * A simple list showing products. This list is used in all following examples. + * + * + * + * + * + * [@type].png + * + * + * + * + * + * + * + * + * + * + * + * + * + * Example: + * This example selects a product by it's value and then removes the + * selection. + * + * + * + * Example: + * This example gets a product by it's value and then removes it. + * + * + * var xmlNode = myList.findXmlNodeByValue("product20"); + * myList.remove(xmlNode); + * + * + * Example: + * This example retrieves all nodes from a list. All items with a length + * greater than 10 are singled out and removed. + * + * 10) + * removeList.push(list[i]); + * } + * myList.remove(removeList); + * } + * ]]> + * + * Remarks: + * Another way to trigger this method is by using the action attribute on a + * button. + * + * Remove item + * + * Using the action methodology you can let the original data source + * (usually the server) know that the user removed an item. + * + * + * + * + * + * + * For undo this action should be extended and the server should maintain a + * copy of the deleted item. + * + * + * + * + * + * Remove item + * + * + * @action + * @param {mixed} [nodeList] the {@link term.datanode data node}(s) to be removed. If none are specified, the current selection is removed. + * Possible values: + * {NodeList} the {@link term.datanode data nodes} to be removed. + * {XMLElement} the {@link term.datanode data node} to be removed. + * @return {Boolean} specifies if the removal succeeded + */ + this.remove = function(nodeList){ + //Use the current selection if no xmlNode is defined + if (!nodeList) + nodeList = this.$valueList; + + //If we're an xml node let's convert + if (nodeList.nodeType) + nodeList = [nodeList]; + + //If there is no selection we'll exit, nothing to do + if (!nodeList || !nodeList.length) + return; + + + + var changes = []; + for (var i = 0; i < nodeList.length; i++) { + changes.push({ + action : "removeNode", + args : [nodeList[i]] + }); + } + + if (this.$actions["removegroup"]) + return this.$executeAction("multicall", changes, "removegroup", nodeList[0]); + else { + return this.$executeAction("multicall", changes, "remove", + nodeList[0], null, null, nodeList.length > 1 ? nodeList : null); + } + }; + + /** + * Adds an {@link term.datanode data node} to the data of this element. + * Example: + * A simple list showing products. This list is used in all following examples. + * + * + * + * + * + * [@type].png + * + * + * + * + * + * + * + * + * + * + * + * + * + * Example: + * This example adds a product to this element. + * selection. + * + * '); + * } + * ]]> + * + * Example: + * This example copy's the selected product, changes it's name and then + * adds it. After selecting the new node the user is offered a rename input + * box. + * + * + * + * Remarks: + * Another way to trigger this method is by using the action attribute on a + * button. + * + * + * + * + * + * + * + * + * + * + * Add new product + * + * Using the action methodology you can let the original data source + * (usually the server) know that the user added an item. + * + * + * + * For undo this action should be extended as follows. + * + * + * + * + * + * + * + * + * + * + * Add new product + * + * + * In some cases the server needs to create the new product before it's + * added. This is done as follows. + * + * + * + * Alternatively the template for the addition can be provided as a child of + * the action rule. + * + * + * + * + * + * @action + * @param {XMLElement} [xmlNode] the {@link term.datanode data node} which is added. If none is specified the action will use the action rule to try to retrieve a new node to add. + * @param {XMLElement} [pNode] the parent node of the added {@link term.datanode data node}. + * @param {XMLElement} [beforeNode] the position where the xml element should be inserted. + * @return {XMLElement} the added {@link term.datanode data node} or false on failure. + */ + this.add = function(xmlNode, pNode, beforeNode, userCallback){ + var rule; + + if (this.$actions) { + if (xmlNode && xmlNode.nodeType) + rule = this.$actions.getRule("add", xmlNode); + else if (typeof xmlNode == "string") { + if (xmlNode.trim().charAt(0) == "<") { + xmlNode = apf.getXml(xmlNode); + rule = this.$actions.getRule("add", xmlNode); + } + else { + var rules = this.$actions["add"]; + for (var i = 0, l = rules.length; i < l; i++) { + if (rules[i].getAttribute("type") == xmlNode) { + xmlNode = null; + rule = rules[i]; + break; + } + } + } + } + + if (!rule) + rule = (this.$actions["add"] || {})[0]; + } + else + rule = null; + + + + var refNode = this.$isTreeArch ? this.selected || this.xmlRoot : this.xmlRoot, + amlNode = this, + callback = function(addXmlNode, state, extra){ + if (state != apf.SUCCESS) { + var oError; + + oError = new Error(apf.formatErrorString(1032, amlNode, + "Loading xml data", + "Could not add data for control " + amlNode.name + + "[" + amlNode.tagName + "] \nUrl: " + extra.url + + "\nInfo: " + extra.message + "\n\n" + xmlNode)); + + if (extra.tpModule.retryTimeout(extra, state, amlNode, oError) === true) + return true; + + throw oError; + } + + /*if (apf.supportNamespaces && node.namespaceURI == apf.ns.xhtml) { + node = apf.getXml(node.xml.replace(/xmlns\=\"[^"]*\"/g, "")); + //@todo import here for webkit? + }*/ + + if (typeof addXmlNode != "object") + addXmlNode = apf.getXmlDom(addXmlNode).documentElement; + if (addXmlNode.getAttribute(apf.xmldb.xmlIdTag)) + addXmlNode.setAttribute(apf.xmldb.xmlIdTag, ""); + + var actionNode = amlNode.$actions && + amlNode.$actions.getRule("add", amlNode.$isTreeArch + ? amlNode.selected + : amlNode.xmlRoot); + if (!pNode) { + if (actionNode && actionNode.parent) { + pNode = (actionNode.cparent + || actionNode.compile("parent", { + xpathmode : 2, + injectself : true + }))(amlNode.$isTreeArch + ? amlNode.selected || amlNode.xmlRoot + : amlNode.xmlRoot); + } + else { + pNode = amlNode.$isTreeArch + ? amlNode.selected || amlNode.xmlRoot + : amlNode.xmlRoot + } + } + + if (!pNode) + pNode = amlNode.xmlRoot; + + //Safari issue not auto importing nodes: + if (apf.isWebkit && pNode.ownerDocument != addXmlNode.ownerDocument) + addXmlNode = pNode.ownerDocument.importNode(addXmlNode, true); + + + + if (amlNode.$executeAction("appendChild", + [pNode, addXmlNode, beforeNode], "add", addXmlNode) !== false + && amlNode.autoselect) + amlNode.select(addXmlNode); + + if (userCallback) + userCallback.call(amlNode, addXmlNode); + + return addXmlNode; + }; + + if (xmlNode) + return callback(xmlNode, apf.SUCCESS); + else { + if (rule.get) + return apf.getData(rule.get, {xmlNode: refNode, callback: callback}) + else { + + } + } + + return addXmlNode; + }; + + if (!this.setValue) { + /** + * Sets the value of this element.The value + * corresponds to an item in the list of loaded {@link term.datanode data nodes}. This + * element will receive the selection. If no {@link term.datanode data node} is found, the + * selection is cleared. + * + * @param {String} value the new value for this element. + * @param {Boolean} disable_event + * @see baseclass.multiselect.method.getValue + */ + this.setValue = function(value, disable_event){ + // @todo apf3.0 what does noEvent do? in this scope it's useless and + // doesn't improve codeflow with a global lookup and assignment + noEvent = disable_event; + this.setProperty("value", value, false, true); + noEvent = false; + }; + } + + /** + * Retrieves an {@link term.datanode data node} that has a value that corresponds to the + * string that is searched on. + * @param {String} value the value to match. + */ + this.findXmlNodeByValue = function(value){ + var nodes = this.getTraverseNodes(), + bindSet = this.$attrBindings["eachvalue"] + && "eachvalue" || this.$bindings["value"] + && "value" || this.$hasBindRule("caption") && "caption"; + + if (!bindSet) + return false; + + for (var i = 0; i < nodes.length; i++) { + if (this.$applyBindRule(bindSet, nodes[i]) == value) + return nodes[i]; + } + }; + + if (!this.getValue) { + /** + * Retrieves the value of this element. This is the value of the + * first selected {@link term.datanode data node}. + * @see #setValue + */ + this.getValue = function(xmlNode, noError){ + return this.value; + /* + if (!this.bindingRules && !this.caption) + return false; + + + + return this.$applyBindRule(this.$mainBind, xmlNode || this.selected, null, true) + || this.$applyBindRule("caption", xmlNode || this.selected, null, true); + */ + }; + } + + /** + * Select the current selection again. + * + * @todo Add support for multiselect + */ + this.reselect = function(){ + if (this.selected) this.select(this.selected, null, this.ctrlselect, + null, true);//no support for multiselect currently. + }; + + /** + * Selects a single, or set of {@link term.eachnode each nodes}. + * The selection can be visually represented in this element. + * + * @param {mixed} xmlNode the identifier to determine the selection. + * Possible values: + * {XMLElement} the {@link term.datanode data node} to be used in the selection as a start/end point or to toggle the selection on the node. + * {HTMLElement} the html element node used as visual representation of {@link term.datanode data node}. Used to determine the {@link term.datanode data node} for selection. + * {String} the value of the {@link term.datanode data node} to be select. + * @param {Boolean} [ctrlKey] whether the Ctrl key was pressed + * @param {Boolean} [shiftKey] whether the Shift key was pressed + * @param {Boolean} [fakeselect] whether only visually a selection is made + * @param {Boolean} [force] whether reselect is forced. + * @param {Boolean} [noEvent] whether to not call any events + * @return {Boolean} whether the selection could be made + * + * @event beforeselect Fires before a {@link baseclass.multiselect.method.select selection} is made + * object: + * {XMLElement} selected the {@link term.datanode data node} that will be selected. + * {Array} selection an array of {@link term.datanode data node} that will be selected. + * {HTMLElement} htmlNode the html element that visually represents the {@link term.datanode data node}. + * @event afterselect Fires after a {@link baseclass.multiselect.method.select selection} is made + * object: + * {XMLElement} selected the {@link term.datanode data node} that was selected. + * {Array} selection an array of {@link term.datanode data node} that are selected. + * {HTMLElement} htmlNode the html element that visually represents the {@link term.datanode data node}. + */ + this.select = function(xmlNode, ctrlKey, shiftKey, fakeselect, force, noEvent, userAction){ + if (!this.selectable || userAction && this.disabled) + return; + + if (parseInt(fakeselect) == fakeselect) { + //Don't select on context menu + if (fakeselect == 2) { + fakeselect = true; + userAction = true; + } + else { + fakeselect = false; + userAction = true; + } + } + + if (this.$skipSelect) { + this.$skipSelect = false; + return; + } + + if (this.ctrlselect && !shiftKey) + ctrlKey = true; + + if (!this.multiselect) + ctrlKey = shiftKey = false; + + // Selection buffering (for async compatibility) + if (!this.xmlRoot) { + if (!this.$buffered) { + var f; + this.addEventListener("afterload", f = function(){ + this.select.apply(this, this.$buffered); + this.removeEventListener("afterload", f); + delete this.$buffered; + }); + } + + this.$buffered = Array.prototype.slice.call(arguments); + return; + } + + var htmlNode; + + /**** Type Detection ****/ + if (!xmlNode) { + + + return false; + } + + if (typeof xmlNode != "object") { + var str = xmlNode; xmlNode = null; + if (typeof xmlNode == "string") + xmlNode = apf.xmldb.getNodeById(str); + + //Select based on the value of the xml node + if (!xmlNode) { + xmlNode = this.findXmlNodeByValue(str); + if (!xmlNode) { + this.clearSelection(noEvent); + return; + } + } + } + + if (!(typeof (xmlNode.style || "") == "object")) { + htmlNode = this.$findHtmlNode(xmlNode.getAttribute( + apf.xmldb.xmlIdTag) + "|" + this.$uniqueId); + } + else { + var id = (htmlNode = xmlNode).getAttribute(apf.xmldb.htmlIdTag); + while (!id && htmlNode.parentNode) + id = (htmlNode = htmlNode.parentNode).getAttribute( + apf.xmldb.htmlIdTag); + + xmlNode = apf.xmldb.getNodeById(id);//, this.xmlRoot); + } + + if (!shiftKey && !ctrlKey && !force && !this.reselectable + && this.$valueList.length <= 1 && this.$valueList.indexOf(xmlNode) > -1) + return; + + if (this.dispatchEvent('beforeselect', { + selected : xmlNode, + htmlNode : htmlNode, + ctrlKey : ctrlKey, + shiftKey : shiftKey, + force : force, + captureOnly : noEvent + }) === false) + return false; + + /**** Selection ****/ + + var lastIndicator = this.caret; + this.caret = xmlNode; + + //Multiselect with SHIFT Key. + if (shiftKey) { + var range = this.$calcSelectRange( + this.$valueList[0] || lastIndicator, xmlNode); + + if (this.$caret) + this.$deindicate(this.$caret); + + this.selectList(range); + + this.$selected = + this.$caret = this.$indicate(htmlNode); + } + else if (ctrlKey) { //Multiselect with CTRL Key. + //Node will be unselected + if (this.$valueList.contains(xmlNode)) { + if (this.selected == xmlNode) { + this.$deselect(this.$findHtmlNode(this.selected.getAttribute( + apf.xmldb.xmlIdTag) + "|" + this.$uniqueId)); + + this.$deindicate(this.$caret); + + if (this.$valueList.length && !fakeselect) { + //this.$selected = this.$selectedList[0]; + this.selected = this.$valueList[0]; + } + } + else + this.$deselect(htmlNode, xmlNode); + + if (!fakeselect) { + this.$selectedList.remove(htmlNode); + this.$valueList.remove(xmlNode); + } + + if (htmlNode != this.$caret) + this.$deindicate(this.$caret); + + this.$selected = + this.$caret = this.$indicate(htmlNode); + } + // Node will be selected + else { + if (this.$caret) + this.$deindicate(this.$caret); + this.$caret = this.$indicate(htmlNode, xmlNode); + + this.$selected = this.$select(htmlNode, xmlNode); + this.selected = xmlNode; + + if (!fakeselect) { + this.$selectedList.push(htmlNode); + this.$valueList.push(xmlNode); + } + } + } + else if (fakeselect && htmlNode && this.$selectedList.contains(htmlNode)) {//Return if selected Node is htmlNode during a fake select + return; + } + else { //Normal Selection + //htmlNode && this.$selected == htmlNode && this.$valueList.length <= 1 && this.$selectedList.contains(htmlNode) + if (this.$selected) + this.$deselect(this.$selected); + if (this.$caret) + this.$deindicate(this.$caret); + if (this.selected) + this.clearSelection(true); + + this.$caret = this.$indicate(htmlNode, xmlNode); + this.$selected = this.$select(htmlNode, xmlNode); + this.selected = xmlNode; + + this.$selectedList.push(htmlNode); + this.$valueList.push(xmlNode); + } + + if (this.delayedselect && (typeof ctrlKey == "boolean")){ + var _self = this; + $setTimeout(function(){ + if (_self.selected == xmlNode) + _self.dispatchEvent("afterselect", { + selection : _self.$valueList, + selected : xmlNode, + caret : _self.caret, + captureOnly : noEvent + }); + }, 10); + } + else { + this.dispatchEvent("afterselect", { + selection : this.$valueList, + selected : xmlNode, + caret : this.caret, + captureOnly : noEvent + }); + } + + return true; + }; + + /** + * Choose a selected item. This is done by double clicking on the item or + * pressing the Enter key. + * + * @param {mixed} xmlNode the identifier to determine the selection. + * Possible values: + * {XMLElement} the {@link term.datanode data node} to be choosen. + * {HTMLElement} the html element node used as visual representation of {@link term.datanode data node}. Used to determine the {@link term.datanode data node}. + * {String} the value of the {@link term.datanode data node} to be choosen. + * @event beforechoose Fires before a choice is made. + * object: + * {XMLElement} xmlNode the {@link term.datanode data node} that was choosen. + * @event afterchoose Fires after a choice is made. + * object: + * {XMLElement} xmlNode the {@link term.datanode data node} that was choosen. + */ + this.choose = function(xmlNode, userAction){ + if (!this.selectable || userAction && this.disabled) return; + + if (this.dispatchEvent("beforechoose", {xmlNode : xmlNode}) === false) + return false; + + if (xmlNode && !(typeof (xmlNode.style || "") == "object")) + this.select(xmlNode); + + + if (this.hasFeature(apf.__DATABINDING__) + && this.dispatchEvent("afterchoose", {xmlNode : this.selected}) !== false) + this.setProperty("chosen", this.selected); + + }; + + /** + * Removes the selection of one or more selected nodes. + * + * @param {Boolean} [singleNode] whether to only deselect the indicated node + * @param {Boolean} [noEvent] whether to not call any events + */ + this.clearSelection = function(noEvent, userAction){ + if (!this.selectable || userAction && this.disabled || !this.$valueList.length) + return; + + if (!noEvent) { + if (this.dispatchEvent("beforeselect", { + selection : [], + selected : null, + caret : this.caret + }) === false) + return false; + } + + //Deselect html nodes + var htmlNode; + for (var i = this.$valueList.length - 1; i >= 0; i--) { + htmlNode = this.$findHtmlNode(this.$valueList[i].getAttribute( + apf.xmldb.xmlIdTag) + "|" + this.$uniqueId); + this.$deselect(htmlNode); + } + + //Reset internal variables + this.$selectedList.length = 0; + this.$valueList.length = 0; + this.$selected = + this.selected = null; + + //Redraw indicator + if (this.caret) { + htmlNode = this.$findHtmlNode(this.caret.getAttribute( + apf.xmldb.xmlIdTag) + "|" + this.$uniqueId); + + this.$caret = this.$indicate(htmlNode); + } + + if (!noEvent) { + this.dispatchEvent("afterselect", { + selection : this.$valueList, + selected : null, + caret : this.caret + }); + } + }; + + /** + * Selects a set of items + * + * @param {Array} xmlNodeList the {@link term.datanode data nodes} that will be selected. + */ + //@todo I think there are missing events here? + this.selectList = function(xmlNodeList, noEvent, selected, userAction){ + if (!this.selectable || userAction && this.disabled) return; + + if (this.dispatchEvent("beforeselect", { + selection : xmlNodeList, + selected : selected || xmlNodeList[0], + caret : this.caret, + captureOnly : noEvent + }) === false) + return false; + + this.clearSelection(true); + + for (var sel, i = 0; i < xmlNodeList.length; i++) { + //@todo fix select state in unserialize after removing + if (!xmlNodeList[i] || xmlNodeList[i].nodeType != 1) continue; + var htmlNode, + xmlNode = xmlNodeList[i]; + + //Type Detection + if (typeof xmlNode != "object") + xmlNode = apf.xmldb.getNodeById(xmlNode); + if (!(typeof (xmlNode.style || "") == "object")) + htmlNode = this.$pHtmlDoc.getElementById(xmlNode.getAttribute( + apf.xmldb.xmlIdTag) + "|" + this.$uniqueId); + else { + htmlNode = xmlNode; + xmlNode = apf.xmldb.getNodeById(htmlNode.getAttribute( + apf.xmldb.htmlIdTag)); + } + + if (!xmlNode) { + + continue; + } + + //Select Node + if (htmlNode) { + if (!sel && selected == htmlNode) + sel = htmlNode; + + this.$select(htmlNode, xmlNode); + this.$selectedList.push(htmlNode); + } + this.$valueList.push(xmlNode); + } + + this.$selected = sel || this.$selectedList[0]; + this.selected = selected || this.$valueList[0]; + + this.dispatchEvent("afterselect", { + selection : this.$valueList, + selected : this.selected, + caret : this.caret, + captureOnly : noEvent + }); + }; + + /** + * Sets the {@link term.caret caret} on an item to indicate to the user that the keyboard + * actions are done relevant to that item. Using the keyboard + * a user can change the position of the indicator using the Ctrl and arrow + * keys while not making a selection. When making a selection with the mouse + * or keyboard the indicator is always set to the selected node. Unlike a + * selection there can be only one indicator item. + * + * @param {mixed} xmlNode the identifier to determine the indicator. + * Possible values: + * {XMLElement} the {@link term.datanode data node} to be set as indicator. + * {HTMLElement} the html element node used as visual representation of + * {@link term.datanode data node}. Used to determine the {@link term.datanode data node}. + * {String} the value of the {@link term.datanode data node} to be set as indicator. + * @event indicate Fires when an item becomes the indicator. + */ + this.setCaret = function(xmlNode){ + if (!xmlNode) { + if (this.$caret) + this.$deindicate(this.$caret); + this.caret = + this.$caret = null; + return; + } + + /**** Type Detection ****/ + var htmlNode; + if (typeof xmlNode != "object") + xmlNode = apf.xmldb.getNodeById(xmlNode); + if (!(typeof (xmlNode.style || "") == "object")) { + htmlNode = this.$findHtmlNode(xmlNode.getAttribute( + apf.xmldb.xmlIdTag) + "|" + this.$uniqueId); + } + else { + var id = (htmlNode = xmlNode).getAttribute(apf.xmldb.htmlIdTag); + while (!id && htmlNode.parentNode && htmlNode.parentNode.nodeType == 1) + id = (htmlNode = htmlNode.parentNode).getAttribute( + apf.xmldb.htmlIdTag); + if (!id) alert(this.$int.outerHTML); + + xmlNode = apf.xmldb.getNodeById(id); + } + + if (this.$caret) { + //this.$deindicate(this.$findHtmlNode(this.caret.getAttribute( + //apf.xmldb.xmlIdTag) + "|" + this.$uniqueId)); + this.$deindicate(this.$caret); + } + + this.$caret = this.$indicate(htmlNode); + this.setProperty("caret", this.caret = xmlNode); + }; + + /** + * @private + */ + this.$setTempSelected = function(xmlNode, ctrlKey, shiftKey, down){ + clearTimeout(this.timer); + + if (this.$bindings.selectable) { + while (xmlNode && !this.$getDataNode("selectable", xmlNode)) { + xmlNode = this.getNextTraverseSelected(xmlNode, !down); + } + if (!xmlNode) return; + } + + if (!this.multiselect) + ctrlKey = shiftKey = false; + + if (ctrlKey || this.ctrlselect) { + if (this.$tempsel) { + this.select(this.$tempsel); + this.$tempsel = null; + } + + this.setCaret(xmlNode); + } + else if (shiftKey){ + if (this.$tempsel) { + this.$selectTemp(); + this.$deselect(this.$tempsel); + this.$tempsel = null; + } + + this.select(xmlNode, null, shiftKey); + } + else if (!this.bufferselect || this.$valueList.length > 1) { + this.select(xmlNode); + } + else { + var id = apf.xmldb.getID(xmlNode, this); + + this.$deselect(this.$tempsel || this.$selected); + this.$deindicate(this.$tempsel || this.$caret); + this.$tempsel = this.$indicate(document.getElementById(id)); + this.$select(this.$tempsel); + + var _self = this; + this.timer = $setTimeout(function(){ + _self.$selectTemp(); + }, 400); + } + }; + + /** + * @private + */ + this.$selectTemp = function(){ + if (!this.$tempsel) + return; + + clearTimeout(this.timer); + this.select(this.$tempsel); + + this.$tempsel = null; + this.timer = null; + }; + + /** + * Selects all the {@link term.eachnode each nodes} of this element + * + */ + this.selectAll = function(userAction){ + if (!this.multiselect || !this.selectable + || userAction && this.disabled || !this.xmlRoot) + return; + + var nodes = this.$isTreeArch + ? this.xmlRoot.selectNodes(".//" + + this.each.split("|").join("|.//")) + : this.xmlRoot.selectNodes(this.each); + + this.selectList(nodes); + }; + + /** + * Retrieves an array or a document fragment containing all the selected + * {@link term.datanode data nodes} from this element. + * + * @param {Boolean} [xmldoc] whether the method should return a document fragment. + * @return {mixed} the selection of this element. + */ + this.getSelection = function(xmldoc){ + var i, r; + if (xmldoc) { + r = this.xmlRoot + ? this.xmlRoot.ownerDocument.createDocumentFragment() + : apf.getXmlDom().createDocumentFragment(); + for (i = 0; i < this.$valueList.length; i++) + apf.xmldb.cleanNode(r.appendChild( + this.$valueList[i].cloneNode(true))); + } + else { + for (r = [], i = 0; i < this.$valueList.length; i++) + r.push(this.$valueList[i]); + } + + return r; + }; + + this.$getSelection = function(htmlNodes){ + return htmlNodes ? this.$selectedList : this.$valueList; + }; + + /** + * Selects the next {@link term.datanode data node} to be selected. + * + * @param {XMLElement} xmlNode the context {@link term.datanode data node}. + * @param {Boolean} isTree + */ + this.defaultSelectNext = function(xmlNode, isTree){ + var next = this.getNextTraverseSelected(xmlNode); + //if(!next && xmlNode == this.xmlRoot) return; + + //Why not use this.$isTreeArch ?? + if (next || !isTree) + this.select(next ? next : this.getTraverseParent(xmlNode)); + else + this.clearSelection(true); + }; + + /** + * Selects the next {@link term.datanode data node} when available. + */ + this.selectNext = function(){ + var xmlNode = this.getNextTraverse(); + if (xmlNode) + this.select(xmlNode); + }; + + /** + * Selects the previous {@link term.datanode data node} when available. + */ + this.selectPrevious = function(){ + var xmlNode = this.getNextTraverse(null, -1); + if (xmlNode) + this.select(xmlNode); + }; + + /** + * @private + */ + this.getDefaultNext = function(xmlNode, isTree){ //@todo why is isTree an argument + var next = this.getNextTraverseSelected(xmlNode); + //if(!next && xmlNode == this.xmlRoot) return; + + return (next && next != xmlNode) + ? next + : (isTree + ? this.getTraverseParent(xmlNode) + : null); //this.getFirstTraverseNode() + }; + + /** + * Determines whether a node is selected. + * + * @param {XMLElement} xmlNode The {@link term.datanode data node} to be checked. + * @return {Boolean} whether the element is selected. + */ + this.isSelected = function(xmlNode){ + if (!xmlNode) return false; + + for (var i = 0; i < this.$valueList.length; i++) { + if (this.$valueList[i] == xmlNode) + return this.$valueList.length; + } + + return false; + }; + + /** + * This function checks whether the current selection is still correct. + * Selection can become invalid when updates to the underlying data + * happen. For instance when a selected node is removed. + */ + this.$checkSelection = function(nextNode){ + if (this.$valueList.length > 1) { + //Fix selection if needed + for (var lst = [], i = 0, l = this.$valueList.length; i < l; i++) { + if (apf.isChildOf(this.xmlRoot, this.$valueList[i])) + lst.push(this.$valueList[i]); + } + + if (lst.length > 1) { + this.selectList(lst); + if(this.caret + && !apf.isChildOf(this.xmlRoot, this.caret)) { + this.setCaret(nextNode || this.selected); + } + return; + } + else if (lst.length) { + //this.clearSelection(true); //@todo noEvents here?? + nextNode = lst[0]; + } + } + + if (!nextNode) { + if (this.selected + && !apf.isChildOf(this.xmlRoot, this.selected)) { + nextNode = this.getFirstTraverseNode(); + } + else if(this.selected && this.caret + && !apf.isChildOf(this.xmlRoot, this.caret)) { + this.setCaret(this.selected); + } + else if (!this.selected){ + nextNode = this.xmlRoot + ? this.getFirstTraverseNode() + : null; + } + else { + return; //Nothing to do + } + } + + if (nextNode) { + if (this.autoselect) { + this.select(nextNode); + } + else { + if (!this.multiselect) + this.clearSelection(); + this.clearSelection(); + this.setCaret(nextNode); + } + } + else + this.clearSelection(); + + //if(action == "synchronize" && this.autoselect) this.reselect(); + }; + + /** + * @attribute {Boolean} [multiselect] whether the user may select multiple items. Default is true, false for dropdown. + * @attribute {Boolean} [autoselect] whether a selection is made after data is loaded. Default is true, false for dropdown. When the string 'all' is set, all {@link term.datanode data nodes} are selected. + * @attribute {Boolean} [selectable] whether the {@link term.datanode data nodes} of this element can be selected. Default is true. + * @attribute {Boolean} [ctrlselect] whether when a selection is made as if the user is holding the Ctrl key. When set to true each mouse selection will add to the current selection. selecting an already selected element will deselect it. + * @attribute {Boolean} [allowdeselect] whether the user can remove the selection of this element. When set to true it is possible for this element to have no selected {@link term.datanode data node}. + * @attribute {Boolean} [reselectable] whether selected nodes can be selected again and the selection events are called again. Default is false. When set to false a selected {@link term.datanode data node} cannot be selected again. + * @attribute {String} [default] the value that this component has when no selection is made. + * @attribute {String} [eachvalue] the {@link term.expression expression} that determines the value for each {@link term.datanode data nodes} in the dataset of the element. + * Remarks: + * + * Example: + * + * @see baseclass.multiselect.attribute.eachvalue + */ + this.selectable = true; + if (typeof this.ctrlselect == "undefined") + this.ctrlselect = false; + if (typeof this.multiselect == "undefined") + this.multiselect = true; + if (typeof this.autoselect == "undefined") + this.autoselect = true; + if (typeof this.delayedselect == "undefined") + this.delayedselect = true; + if (typeof this.allowdeselect == "undefined") + this.allowdeselect = true; + this.reselectable = false; + + this.$booleanProperties["selectable"] = true; + //this.$booleanProperties["ctrlselect"] = true; + this.$booleanProperties["multiselect"] = true; + this.$booleanProperties["autoselect"] = true; + this.$booleanProperties["delayedselect"] = true; + this.$booleanProperties["allowdeselect"] = true; + this.$booleanProperties["reselectable"] = true; + + this.$supportedProperties.push("selectable", "ctrlselect", "multiselect", + "autoselect", "delayedselect", "allowdeselect", "reselectable", + "selection", "selected", "default", "value", "caret"); + + /** + * @attribute {String} [value] the value of the element that is selected. + * Remarks: + * + * Example: + * + * @see baseclass.multiselect.attribute.eachvalue + */ + //@todo add check here + this.$propHandlers["value"] = function(value){ + if (this.$lastValue == value) { + delete this.$lastValue; + return; + } + + if (!this.$attrBindings["eachvalue"] && !this.$amlLoaded + && this.getAttribute("eachvalue")) { + var _self = this; + return apf.queue.add("value" + this.$uniqueId, function(){ + _self.$propHandlers["value"].call(_self, value); + }); + } + + + + if (value || value === 0 || this["default"]) + this.select(String(value) || this["default"]); + else + this.clearSelection(); + } + + this.$propHandlers["default"] = function(value, prop){ + if (!this.value || !this.$amlLoaded && !(this.getAttribute("value") + || this.getAttribute("selected") || this.getAttribute("selection"))) { + this.$propHandlers["value"].call(this, ""); + } + } + + /** + * @attribute {String} [value] the value of the element that is selected. + * Remarks: + * + * Example: + * + * @see baseclass.multiselect.attribute.selected, baseclass.multiselect.attribute.selection + */ + //@todo fill this in + this.$propHandlers["caret"] = function(value, prop){ + if (value) + this.setCaret(value); + } + + + + //@todo optimize this thing. Also implement virtual dataset support. + /** + * @attribute {String} [selection] the {@link term.expression expression} that determines the selection for this element. A reference to an xml nodelist can be passed as well. + * Remarks: + * + * Example: + * + * @see baseclass.multiselect.attribute.selected, baseclass.multiselect.attribute.selection + */ + this.$propHandlers["selection"] = + + /** + * @attribute {String} [selected] the {@link term.expression expression} that determines the selected node for this element. A reference to an xml element can be passed as well. + * Remarks: + * + * Example: + * + * @see baseclass.multiselect.attribute.selected, baseclass.multiselect.attribute.selection + */ + this.$propHandlers["selected"] = function(value, prop) { + if (!value) value = this[prop] = null; + + if (prop == "selected" && typeof value != "string") { // && value == this.selected + if (value && value.nodeType != 1) + value = value.nodeValue; + else + //this.selected = null; //I don't remember why this is here. It removes the selected property without setting it again. (dropdown test) + return; + } + + + + if (this.$isSelecting) { + this.selection = this.$valueList; + return false; + } + + var nodes, bindSet, getValue, i, j, c, d; + //Update the selection + if (prop == "selection") { + if (typeof value == "object" && value == this.$valueList) { + var pNode; + //We're using an external model. Need to update bound nodeset + if ((c = this.$attrBindings[prop]) && c.cvalue.models) { //added check, @todo whats up with above assumption? + this.$isSelecting = true; //Prevent reentrance (optimization) + + bindSet = this.$attrBindings["eachvalue"] + && "eachvalue" || this.$bindings["value"] + && "value" || this.$hasBindRule("caption") && "caption"; + + if (!bindSet) + throw new Error("Missing bind rule set: eachvalue, value or caption");//@todo apf3.0 make this into a proper error + + //@todo this may be optimized by keeping a copy of the selection + var selNodes = this.$getDataNode(prop, this.xmlRoot); + nodes = value; + getValue = (d = this.$attrBindings["selection-unique"]) && d.cvalue; + + if (selNodes.length) { + pNode = selNodes[0].parentNode; + } + else { + var model, path; + if (c.cvalue.xpaths[0] == "#" || c.cvalue.xpaths[1] == "#") { + var m = (c.cvalue3 || (c.cvalue3 = apf.lm.compile(c.value, { + xpathmode: 5 + })))(this.xmlRoot); + + model = m.model && m.model.$isModel && m.model; + if (model) + path = m.xpath; + else if (m.model) { + model = apf.xmldb.findModel(m.model); + path = apf.xmlToXpath(m.model, model.data) + (m.xpath ? "/" + m.xpath : ""); //@todo make this better + } + else { + //No selection - nothing to do + } + } + else { + + model = apf.nameserver.get("model", c.cvalue.xpaths[0]); + + path = c.cvalue.xpaths[1]; + } + + if (!model || !model.data) { + this.$isSelecting = false; + return false; + } + + pNode = model.queryNode(path.replace(/\/[^\/]+$|^[^\/]*$/, "") || "."); + + if (!pNode) + throw new Error("Missing parent node"); //@todo apf3.0 make this into a proper error + } + + //Nodes removed + remove_loop: + for (i = 0; i < selNodes.length; i++) { + //Value is either determined by special property or in the + //same way as the value for the bound node. + value = getValue + ? getValue(selNodes[i]) + : this.$applyBindRule(bindSet, selNodes[i]); + + //Compare the value with the traverse nodes + for (j = 0; j < nodes.length; j++) { + if (this.$applyBindRule(bindSet, nodes[j]) == value) //@todo this could be cached + continue remove_loop; + } + + //remove node + apf.xmldb.removeNode(selNodes[i]); + } + + //Nodes added + add_loop: + for (i = 0; i < nodes.length; i++) { + //Value is either determined by special property or in the + //same way as the value for the bound node. + value = this.$applyBindRule(bindSet, nodes[i]); + + //Compare the value with the traverse nodes + for (j = 0; j < selNodes.length; j++) { + if (getValue + ? getValue(selNodes[j]) + : this.$applyBindRule(bindSet, selNodes[j]) == value) //@todo this could be cached + continue add_loop; + } + + //add node + var node = this.$attrBindings["selection-constructor"] + && this.$getDataNode("selection-constructor", nodes[i]) + || apf.getCleanCopy(nodes[i]); + apf.xmldb.appendChild(pNode, node); + } + + //@todo above changes should be via the actiontracker + this.$isSelecting = false; + } + + return; + } + this.selection = this.$valueList; + } + else { + this.selected = null; + } + + if (!this.xmlRoot) { + if (!this.$buffered) { + var f; + this.addEventListener("afterload", f = function(){ + this.removeEventListener("afterload", f); + this.$propHandlers["selected"].call(this, value, prop); + delete this.$buffered; + }); + this.$buffered = true; + } + this[prop] = null; + return false; + } + + if (!value || typeof value != "object") { + //this[prop] = null; + + if (this.$attrBindings[prop]) { + //Execute the selection query + nodes = this.$getDataNode(prop, this.xmlRoot); + if (nodes && (nodes.length || nodes.nodeType == 1)) { + this.setProperty("selection", nodes); + return; + } + + if (!nodes || nodes.length === 0) + return; + + //Current model, it's an init selection, we'll clear the bind + /*if (typeof value == "string" + && !this.$attrBindings[prop].cvalue.xpaths[0]) { + this.$removeAttrBind(prop); + }*/ + } + + if (!value) { + this.clearSelection(); + } + else { + this.select(value); + } + + return false; //Disable signalling the listeners to this property + } + else if (typeof value.length == "number") { + nodes = value; + if (!nodes.length) { + this.selected = null; + if (this.$valueList.length) { //dont clear selection when no selection exists (at prop init) + this.clearSelection(); + return false; //Disable signalling the listeners to this property + } + else return; + } + + //For when nodes are traverse nodes of this element + if (this.isTraverseNode(nodes[0]) + && apf.isChildOf(this.xmlRoot, nodes[0])) { + if (!this.multiselect) { + this.select(nodes[0]); + } + else { + //this[prop] = null; //?? + this.selectList(nodes); + } + return false; //Disable signalling the listeners to this property + } + + //if external model defined, loop through items and find mate by value + if (this.$attrBindings[prop]) { //Can assume an external model is in place + bindSet = this.$attrBindings["eachvalue"] + && "eachvalue" || this.$bindings["value"] + && "value" || this.$hasBindRule("caption") && "caption"; + + if (!bindSet) + throw new Error("Missing bind rule set: eachvalue, value or caption");//@todo apf3.0 make this into a proper error + + var tNodes = !this.each + ? this.getTraverseNodes() + : this.xmlRoot.selectNodes("//" + this.each.split("|").join("|//")); + + getValue = (c = this.$attrBindings["selection-unique"]) && c.cvalue; + var selList = []; + for (i = 0; i < nodes.length; i++) { + //Value is either determined by special property or in the + //same way as the value for the bound node. + value = getValue + ? getValue(nodes[i]) + : this.$applyBindRule(bindSet, nodes[i]); + + //Compare the value with the traverse nodes + for (j = 0; j < tNodes.length; j++) { + if (this.$applyBindRule(bindSet, tNodes[j]) == value) //@todo this could be cached + selList.push(tNodes[j]); + } + } + + //this[prop] = null; //??? + this.selectList(selList, true); //@todo noEvent to distinguish between user actions and not user actions... need to rethink this + return false; + } + + throw new Error("Show me which case this is"); + } + else if (this.$valueList.indexOf(value) == -1) { + //this.selected = null; + this.select(value); + } + }; + + + + this.$propHandlers["allowdeselect"] = function(value){ + if (value) { + var _self = this; + this.$container.onmousedown = function(e){ + if (!e) + e = event; + if (e.ctrlKey || e.shiftKey) + return; + + var srcElement = e.srcElement || e.target; + if (_self.allowdeselect && (srcElement == this + || srcElement.getAttribute(apf.xmldb.htmlIdTag))) + _self.clearSelection(); //hacky + } + } + else { + this.$container.onmousedown = null; + } + }; + + this.$propHandlers["ctrlselect"] = function(value){ + if (value != "enter") + this.ctrlselect = apf.isTrue(value); + } + + function fAutoselect(){ + this.selectAll(); + } + + this.$propHandlers["autoselect"] = function(value){ + if (value == "all" && this.multiselect) + this.addEventListener("afterload", fAutoselect); + else + this.removeEventListener("afterload", fAutoselect); + }; + + this.$propHandlers["multiselect"] = function(value){ + if (!value && this.$valueList.length > 1) + this.select(this.selected); + + //if (value) + //this.bufferselect = false; //@todo doesn't return to original value + }; + + // Select Bind class + + this.addEventListener("beforeselect", function(e){ + if (this.$bindings.selectable && !this.$getDataNode("selectable", e.selected)) + return false; + }, true); + + + + this.addEventListener("afterselect", function (e){ + + var combinedvalue = null; + + + //@todo refactor below + /*if (this.caret == this.selected || e.list && e.list.length > 1 && hasConnections) { + //Multiselect databinding handling... [experimental] + if (e.list && e.list.length > 1 && this.$getConnections().length) { //@todo this no work no more apf3.0 + var oEl = this.xmlRoot.ownerDocument.createElement(this.selected.tagName); + var attr = {}; + + //Fill basic nodes + var nodes = e.list[0].attributes; + for (var j = 0; j < nodes.length; j++) + attr[nodes[j].nodeName] = nodes[j].nodeValue; + + //Remove nodes + for (var prop, i = 1; i < e.list.length; i++) { + for (prop in attr) { + if (typeof attr[prop] != "string") continue; + + if (!e.list[i].getAttributeNode(prop)) + attr[prop] = undefined; + else if(e.list[i].getAttribute(prop) != attr[prop]) + attr[prop] = ""; + } + } + + //Set attributes + for (prop in attr) { + if (typeof attr[prop] != "string") continue; + oEl.setAttribute(prop, attr[prop]); + } + + //missing is childnodes... will implement later when needed... + + oEl.setAttribute(apf.xmldb.xmlIdTag, this.$uniqueId); + apf.MultiSelectServer.register(oEl.getAttribute(apf.xmldb.xmlIdTag), + oEl, e.list, this); + apf.xmldb.addNodeListener(oEl, apf.MultiSelectServer); + + combinedvalue = oEl; + } + }*/ + + + //Set caret property + this.setProperty("caret", e.caret); + + //Set selection length + if (this.sellength != e.selection.length) + this.setProperty("sellength", e.selection.length); + + //Set selection property + delete this.selection; + this.setProperty("selection", e.selection); + if (!e.selection.length) { + //Set selected property + this.setProperty("selected", e.selected); + + //Set value property + if (this.value) + this.setProperty("value", ""); + } + else { + //Set selected property + this.$chained = true; + if (!e.force && (!this.dataParent || !this.dataParent.parent.$chained)) { + var _self = this; + $setTimeout(function(){ + + if (_self.selected == e.selected) + _self.setProperty("selected", combinedvalue || e.selected); + + delete _self.$chained; + }, 10); + } + else { + + this.setProperty("selected", combinedvalue || e.selected); + + delete this.$chained; + } + + //Set value property + var valueRule = this.$attrBindings["eachvalue"] && "eachvalue" + || this.$bindings["value"] && "value" + || this.$hasBindRule("caption") && "caption"; + + if (valueRule) { + //@todo this will call the handler again - should be optimized + + this.$lastValue = this.$applyBindRule(valueRule, e.selected) + //this.$attrBindings["value"] && + if (this.$lastValue != + (valueRule != "value" && (this.xmlRoot + && this.$applyBindRule("value", this.xmlRoot, null, true)) + || this.value)) { + if (valueRule == "eachvalue" || this.xmlRoot != this) + this.change(this.$lastValue); + else + this.setProperty("value", this.$lastValue); + } + /*else { + this.setProperty("value", this.$lastValue); + }*/ + delete this.$lastValue; + } + } + + + + + }, true); + + + + +}).call(apf.MultiSelect.prototype = new apf.MultiselectBinding()); + + + +//@todo refactor below +/** + * @private + */ +/* +apf.MultiSelectServer = { + objects : {}, + + register : function(xmlId, xmlNode, selList, jNode){ + if (!this.$uniqueId) + this.$uniqueId = apf.all.push(this) - 1; + + this.objects[xmlId] = { + xml : xmlNode, + list : selList, + jNode : jNode + }; + }, + + $xmlUpdate : function(action, xmlNode, listenNode, UndoObj){ + if (action != "attribute") return; + + var data = this.objects[xmlNode.getAttribute(apf.xmldb.xmlIdTag)]; + if (!data) return; + + var nodes = xmlNode.attributes; + + for (var j = 0; j < data.list.length; j++) { + //data[j].setAttribute(UndoObj.name, xmlNode.getAttribute(UndoObj.name)); + apf.xmldb.setAttribute(data.list[j], UndoObj.name, + xmlNode.getAttribute(UndoObj.name)); + } + + //apf.xmldb.synchronize(); + } +}; +*/ + + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/core/baseclasses/childvalue.js)SIZE(3934)TIME(Fri, 14 Oct 2011 08:01:44 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + +apf.__CHILDVALUE__ = 1 << 27; + + +apf.ChildValue = function(){ + if (!this.$childProperty) + this.$childProperty = "value"; + + this.$regbase = this.$regbase | apf.__CHILDVALUE__; + + var f, re = /^[\s\S]*?>(<\?lm)?([\s\S]*?)(?:\?>)?<[^>]*?>$/; + this.addEventListener("DOMCharacterDataModified", f = function(e){ + if (e && (e.currentTarget == this + || e.currentTarget.nodeType == 2 && e.relatedNode == this) + || this.$amlDestroyed) + return; + + if (this.getAttribute(this.$childProperty)) + return; + + //Get value from xml (could also serialize children, but that is slower + var m = this.serialize().match(re), + v = m && m[2] || ""; + if (m && m[1]) + v = "{" + v + "}"; + + this.$norecur = true; + + + if (v.indexOf("{") > -1 || v.indexOf("[") > -1) + this.$setDynamicProperty(this.$childProperty, v); + else + + if (this[this.$childProperty] != v) + this.setProperty(this.$childProperty, v); + + this.$norecur = false; + }); + + //@todo Should be buffered + this.addEventListener("DOMAttrModified", f); + this.addEventListener("DOMNodeInserted", f); + this.addEventListener("DOMNodeRemoved", f); + + this.addEventListener("$skinchange", function(e){ + this.$propHandlers[this.$childProperty].call(this, this.caption || ""); + }); + + this.$init(function(){ + this.addEventListener("prop." + this.$childProperty, function(e){ + if (!this.$norecur && !e.value && !this.getAttributeNode(this.$childProperty)) + f.call(this); + }); + }); + + this.addEventListener("DOMNodeInsertedIntoDocument", function(e){ + var hasNoProp = typeof this[this.$childProperty] == "undefined"; + + //this.firstChild.nodeType != 7 && + if (hasNoProp + && !this.getElementsByTagNameNS(this.namespaceURI, "*", true).length + && (this.childNodes.length > 1 || this.firstChild + && (this.firstChild.nodeType == 1 + || this.firstChild.nodeValue.trim().length))) { + //Get value from xml (could also serialize children, but that is slower + var m = (this.$aml && this.$aml.xml || this.serialize()).match(re), + v = m && m[2] || ""; + if (m && m[1]) + v = "{" + v + "}"; + + + if (v.indexOf("{") > -1 || v.indexOf("[") > -1) + this.$setDynamicProperty(this.$childProperty, v); + else + + this.setProperty(this.$childProperty, apf.html_entity_decode(v)); //@todo should be xml entity decode + } + else if (hasNoProp) + this.$propHandlers[this.$childProperty].call(this, ""); + }); +}; + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/core/baseclasses/dataaction.js)SIZE(27055)TIME(Fri, 14 Oct 2011 08:01:44 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + +apf.__DATAACTION__ = 1 << 25; + + +/** + * Baseclass adding data action features to this element. + */ +apf.DataAction = function(){ + this.$regbase = this.$regbase | apf.__DATAACTION__; + + /**** Public Methods ****/ + + /** + * Gets the ActionTracker this element communicates with. + * + * @return {ActionTracker} + * @see element.smartbinding + */ + this.getActionTracker = function(ignoreMe){ + if (!apf.AmlNode) + return apf.window.$at; + + var pNode = this, tracker = ignoreMe ? null : this.$at; + if (!tracker && this.dataParent) + tracker = this.dataParent.parent.$at; //@todo apf3.0 change this to be recursive?? + + while (!tracker) { + if (!pNode.parentNode && !pNode.$parentNode) { + var model; + return (model = this.getModel && this.getModel(true)) && model.$at || apf.window.$at; + } + + tracker = (pNode = pNode.parentNode || pNode.$parentNode).$at; + } + return tracker; + }; + + + + this.$actionsLog = {}; + this.$actions = false; + + /** + * @term locking {@link http://en.wikipedia.org/wiki/Lock_(computer_science) A lock} + * is a mechanism for enforcing limits on access to a resource in a + * multi-user environment. Locks are one way of enforcing concurrency + * control policies. Ajax.org Platform (apf) has support for locking in + * combination with {@link term.action action rules}. There are two + * types of locks; pessimistic and optimistic locks. Descriptions below are + * from {@link http://en.wikipedia.org/wiki/Lock_(computer_science) wikipedia}. + * + * Optimistic: + * This allows multiple concurrent users access to the database whilst the + * system keeps a copy of the initial-read made by each user. When a user + * wants to update a record, the application determines whether another user + * has changed the record since it was last read. The application does this + * by comparing the initial-read held in memory to the database record to + * verify any changes made to the record. Any discrepancies between the + * initial-read and the database record violates concurrency rules and hence + * causes the system to disregard any update request. An error message is + * generated and the user is asked to start the update process again. + * It improves database performance by reducing the amount of locking + * required, thereby reducing the load on the database server. It works + * efficiently with tables that require limited updates since no users are + * locked out. However, some updates may fail. The downside is constant + * update failures due to high volumes of update requests from multiple + * concurrent users - it can be frustrating for users. + * + * For optimistic locking apf can run as if there would be no locking. + * Changed data is sent to the server and is either successfully saved or + * not. When the action isn't changed and the server returns an error code + * the {@link element.actiontracker actiontracker} automatically + * reverts the change. + * + * Pessimistic: + * This is whereby a user who reads a record with the intention of updating + * it, places an exclusive lock on the record to prevent other users from + * manipulating it. This means no one else can manipulate that record until + * the user releases the lock. The downside is that users can be locked out + * for a long time thereby causing frustration. + * + * For pessimistic locking add the locking attribute to the {@link term.action action rules} + * that need it. The following example shows a lock request for a rename + * action on a file browser tree. + * + * + * + * The unlock variable is true when the lock needs to be released. This is + * done when the action was cancelled after getting a lock. For instance + * when the user presses escape while renaming. + * + * MultiUser: + * In multi user environments it can be handy + * to be signalled of changes by others within the application. For more + * information on this please look at {@link element.remote}. + * + * Remarks: + * During offline works pessimistic locks will always fail. If the application + * does not use {@link element.remote remote smart bindings} the developer + * should reload the part of the content for which the lock failed. See + * {@link baseclass.databinding.event.lockfailed}. + * + * Note: APF understands the status codes specified in RFC4918 for the locking implementation + * {@link http://tools.ietf.org/html/rfc4918#section-9.10.6} + */ + + /** + * Start the specified action, does optional locking and can be offline aware + * - or for optimistic locking it will record the timestamp (a setting + * ) + * - During offline work, optimistic locks will be handled by taking the + * timestamp of going offline + * - This method is always optional! The server should not expect locking to exist. + * + * @event locksuccess Fires when a lock request succeeds + * bubbles: yes + * object: + * {Number} state the return code of the lock request + * @event lockfailed Fires when a lock request failes + * bubbles: yes + * object: + * {Number} state the return code of the lock request + * @event unlocksuccess Fires when an unlock request succeeds + * bubbles: yes + * object: + * {Number} state the return code of the unlock request + * @event unlockfailed Fires when an unlock request fails + * bubbles: yes + * object: + * {Number} state the return code of the unlock request + */ + this.$startAction = function(name, xmlContext, fRollback){ + if (this.disabled || this.liveedit && name != "edit") + return false; + + var actionRule = this.$actions && this.$actions.getRule(name, xmlContext); + if (!actionRule && apf.config.autoDisableActions && this.$actions) { + + + return false; + } + + var bHasOffline = typeof apf.offline != "undefined"; + + + if (this.dispatchEvent(name + "start", { + xmlContext: xmlContext + }) === false) + return false; + + + + this.$actionsLog[name] = xmlContext; + + return true; + }; + + + + this.$stopAction = function(name, isCancelled, curLock){ + delete this.$actionsLog[name]; + + + }; + + /** + * Executes an action using action rules set in the {@link element.actions actions element}. + * + * @param {String} atAction the name of the action to be performed by the ActionTracker. + * Possible values: + * setTextNode sets the first text node of an xml element. {@link core.xmldb.method.setTextNode} + * setAttribute sets the attribute of an xml element. {@link core.xmldb.method.setAttribute} + * removeAttribute removes an attribute from an xml element. {@link core.xmldb.method.removeAttribute} + * setAttributes sets multiple attribute on an xml element. Arguments are [xmlNode, Array] + * replaceNode replaces an xml child with another one. {@link core.xmldb.method.replaceNode} + * addChildNode adds a new xml node to a parent node. {@link core.xmldb.method.addChildNode} + * appendChild appends an xml node to a parent node. {@link core.xmldb.method.appendChild} + * moveNode moves an xml node from one parent to another. {@link core.xmldb.method.moveNode} + * removeNode removes a node from it's parent. {@link core.xmldb.method.removeNode} + * removeNodeList removes multiple nodes from their parent. {@link core.xmldb.method.removeNodeList} + * setValueByXpath sets the nodeValue of an xml node whiche is selected + * by an xpath statement. Arguments are [xmlNode, xpath, value] + * multicall calls multiple of these actions. Arguments is an array + * of argument arrays for these actions each with a func + * property which is the name of the action. + * @param {Array} args the arguments to the function specified + * in atAction. + * @param {String} action the name of the action rule defined in + * actions for this element. + * @param {XMLElement} xmlNode the context for the action rules. + * @param {Boolean} [noevent] whether or not to call events. + * @param {XMLElement} [contextNode] the context node for action processing + * (such as RPC calls). Usually the same + * as xmlNode + * @return {Boolean} specifies success or failure + * @see element.smartbinding + */ + this.$executeAction = function(atAction, args, action, xmlNode, noevent, contextNode, multiple){ + + + + + //Get Rules from Array + var rule = this.$actions && this.$actions.getRule(action, xmlNode); + if (!rule && this.$actions && apf.config.autoDisableActions + && "action|change".indexOf(action) == -1) { + apf.console.warn("Could not execute action '" + action + "'. \ + No valid action rule was found and auto-disable-actions is enabled"); + + return false; + } + + + + var newMultiple; + if (multiple) { + newMultiple = []; + for (var k = multiple.length - 1; k >= 0; k--) { + newMultiple.unshift({ + xmlActionNode : rule && rule[4], + amlNode : this, + selNode : multiple[k], + xmlNode : multiple[k] + }) + } + } + + //@todo apf3.0 Shouldn't the contextNode be made by the match + var ev = new apf.AmlEvent("before" + action.toLowerCase(), { + action : atAction, + args : args, + xmlActionNode : rule, + amlNode : this, + selNode : contextNode, + multiple : newMultiple || false + + }); + + //Call Event and cancel if it returns false + if (!noevent) { + //Allow the action and arguments to be changed by the event + if (this.dispatchEvent(ev.name, null, ev) === false) + return false; + + delete ev.currentTarget; + } + + //Call ActionTracker and return ID of Action in Tracker + var at = this.getActionTracker(); + if (!at)// This only happens at destruction of apf + return UndoObj; + + var UndoObj = at.execute(ev); + ev.xmlNode = UndoObj.xmlNode; + ev.undoObj = UndoObj; + + //Call After Event + if (!noevent) { //@todo noevent is not implemented for before.. ??? + ev.name = "after" + action.toLowerCase(); + ev.cancelBubble = false; + delete ev.returnValue; + delete ev.currentTarget; + this.dispatchEvent(ev.name, null, ev); + } + + return UndoObj; + }; + + /** + * Executes an action based on the set name and the new value + * @param {String} atName the name of the action rule defined in actions for this element. + * @param {String} setName the name of the binding rule defined in bindings for this element. + * @param {XMLElement} xmlNode the xml element to which the rules are applied + * @param {String} value the new value of the node + */ + this.$executeSingleValue = function(atName, setName, xmlNode, value, getArgList){ + var xpath, args, rule = this.$getBindRule(setName, xmlNode); + + //recompile bindrule to create nodes + if (!rule) { + + return false; + } + + var compiled; + ["valuematch", "match", "value"].each(function(type){ + if (!rule[type] || compiled) + return; + + compiled = rule["c" + type]; //cvaluematch || (rule.value ? rule.cvalue : rule.cmatch); + if (!compiled) + compiled = rule.compile(type); + + if (compiled.type != 3) + compiled = null; + }); + + + + var atAction, model, node, + sel = compiled.xpaths, //get first xpath + shouldLoad = false; + + if (sel[0] == "#" || sel[1] == "#") { + var m = (rule.cvalue3 || (rule.cvalue3 = apf.lm.compile(rule.value, { + xpathmode: 5 + })))(xmlNode); + + model = m.model && m.model.$isModel && m.model; + if (model) { + node = model.queryNode(m.xpath); + xmlNode = model.data; + } + else { + model = apf.xmldb.findModel(m.model); + node = m.model.selectSingleNode(m.xpath); + xmlNode = m.model; + } + + sel[1] = m.xpath; + } + else { + + model = sel[0] && apf.nameserver.get("model", sel[0]) || this.$model, + node = model + ? model.queryNode(sel[1]) + : (xmlNode || this.xmlRoot).selectSingleNode(sel[1]); + if (model && !xmlNode) + xmlNode = model.data; //@experimental, after changing this, please run test/test_rename_edge.html + + } + + if (node) { + if (apf.queryValue(node) == value) return; // Do nothing if value is unchanged + + atAction = (node.nodeType == 1 || node.nodeType == 3 + || node.nodeType == 4) ? "setTextNode" : "setAttribute"; + args = (node.nodeType == 1) + ? [node, value] + : (node.nodeType == 3 || node.nodeType == 4 + ? [node.parentNode, value] + : [node.ownerElement || node.selectSingleNode(".."), node.nodeName, value]); + } + else { + if (!this.$createModel) + return false; + + atAction = "setValueByXpath"; + xpath = sel[1]; + + if (!xmlNode) { + //Assuming this component is connnected to a model + if (!model) + model = this.getModel(); + if (model) { + if (!model.data) + model.load(""); + + xpath = (model.getXpathByAmlNode(this) || ".") + + (xpath && xpath != "." ? "/" + xpath : ""); + xmlNode = model.data; + } + else { + if (!this.dataParent) + return false; + + xmlNode = this.dataParent.parent.selected || this.dataParent.parent.xmlRoot; + if (!xmlNode) + return false; + + xpath = (this.dataParent.xpath || ".") + + (xpath && xpath != "." ? "/" + xpath : ""); + shouldLoad = true; + } + } + + args = [xmlNode, value, xpath]; + } + + if (getArgList) { + return { + action : atAction, + args : args + }; + } + + //Use Action Tracker + var result = this.$executeAction(atAction, args, atName, xmlNode); + + if (shouldLoad) + this.load(xmlNode.selectSingleNode(xpath)); + + return result; + }; + + /** + * Changes the value of this element. + * @action + * @param {String} [string] the new value of this element. + * @todo apf3.0 maybe not for multiselect?? - why is clearError handling not + * in setProperty for value + */ + this.change = function(value, force){ + + if (this.errBox && this.errBox.visible && this.isValid && this.isValid()) + this.clearError(); + + + + //Not databound + if (!this.xmlRoot && !this.$createModel || !(this.$mainBind == "value" + && this.hasFeature(apf.__MULTISELECT__) + ? this.$attrBindings["value"] + : this.$hasBindRule(this.$mainBind))) { + + if (!force && value === this.value + || this.dispatchEvent("beforechange", {value : value}) === false) + return false; + + //@todo in theory one could support actions + //@todo disabled below, because it gives unexpected behaviour when + //form elements are used for layout and other UI alterations + /*this.getActionTracker().execute({ + action : "setProperty", + args : [this, "value", value, false, true], + amlNode : this + });*/ + this.setProperty("value", value); + + return this.dispatchEvent("afterchange", {value : value}); + + } + + var valueRule = this.$attrBindings["eachvalue"] && "eachvalue" + || this.$bindings["value"] && "value" + || this.$hasBindRule("caption") && "caption"; + + if (value === (valueRule != "value" && (this.xmlRoot + && this.$applyBindRule("value", this.xmlRoot, null, true)) + || this.value)) + return false; + + this.$executeSingleValue("change", this.$mainBind, this.xmlRoot, value); + + }; + + this.$booleanProperties["render-root"] = true; + this.$supportedProperties.push("create-model", "actions"); + + /** + * @attribute {Boolean} create-model whether the model this element connects + * to is extended when the data pointed to does not exist. Defaults to true. + * Example: + * In this example a model is extended when the user enters information in + * the form elements. Because no model is specified for the form elements + * the first available model is chosen. At the start it doesn't have any + * data, this changes when for instance the name is filled in. A root node + * is created and under that a 'name' element with a textnode containing + * the entered text. + * + * + * Name + * + * + * Address + * + * + * Country + * + * Submit + * + * + */ + this.$propHandlers["create-model"] = function(value){ + this.$createModel = value; + }; + + this.addEventListener("DOMNodeInsertedIntoDocument", function(e){ + if (typeof this["create-model"] == "undefined" + && !this.$setInheritedAttribute("create-model")) { + this.$createModel = true; + } + }); +}; + +apf.config.$inheritProperties["create-model"] = 1; + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/core/baseclasses/cache.js)SIZE(12532)TIME(Wed, 30 Nov 2011 17:29:20 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + +apf.__CACHE__ = 1 << 2; + + + +/** + * All elements inheriting from this {@link term.baseclass baseclass} have caching features. It takes care of + * storing, retrieving and updating rendered data (in html form) + * to overcome the waiting time while rendering the contents every time the + * data is loaded. + * + * @constructor + * @baseclass + * @author Ruben Daniels (ruben AT ajax DOT org) + * @version %I%, %G% + * @since 0.4 + */ +apf.Cache = function(){ + /* ******************************************************************** + PROPERTIES + *********************************************************************/ + this.cache = {}; + this.$subTreeCacheContext = null; + + this.caching = true; + this.$regbase = this.$regbase | apf.__CACHE__; + + /* ******************************************************************** + PUBLIC METHODS + *********************************************************************/ + + this.addEventListener("$load", function(e){ + if (!this.caching || e.forceNoCache) + return; + + // retrieve the cacheId + if (!this.cacheId) { + this.cacheId = this.$generateCacheId && this.$generateCacheId(e.xmlNode) + || e.xmlNode.getAttribute(apf.xmldb.xmlIdTag) + || apf.xmldb.nodeConnect(apf.xmldb.getXmlDocId(e.xmlNode), e.xmlNode);//e.xmlNode + } + + // Retrieve cached version of document if available + var fromCache = getCache.call(this, this.cacheId, e.xmlNode); + if (fromCache) { + if (fromCache == -1 || !this.getTraverseNodes) + return (e.returnValue = false); + + var nodes = this.getTraverseNodes(); + + //Information needs to be passed to the followers... even when cached... + if (nodes.length) { + if (this["default"]) + this.select(this["default"]); + else if (this.autoselect) + this.select(nodes[0], null, null, null, true); + } + else if (this.clearSelection) + this.clearSelection(); //@todo apf3.0 was setProperty("selected", null + + if (!nodes.length) { + // Remove message notifying user the control is without data + this.$removeClearMessage(); + this.$setClearMessage(this["empty-message"], "empty"); + } + + + //@todo move this to getCache?? + if (nodes.length != this.length) + this.setProperty("length", nodes.length); + + + return false; + } + }); + + this.addEventListener("$clear", function(){ + if (!this.caching) + return; + + /* + Check if we borrowed an HTMLElement + We should return it where it came from + + note: There is a potential that we can't find the exact location + to put it back. We should then look at it's position in the xml. + (but since I'm lazy it's not doing this right now) + There might also be problems when removing the xmlroot + */ + if (this.hasFeature(apf.__MULTISELECT__) + && this.$subTreeCacheContext && this.$subTreeCacheContext.oHtml) { + if (this.renderRoot) { + this.$subTreeCacheContext.parentNode.insertBefore( + this.$subTreeCacheContext.oHtml, this.$subTreeCacheContext.beforeNode); + } + else { + var container = this.$subTreeCacheContext.container || this.$container; + while (container.childNodes.length) + this.$subTreeCacheContext.oHtml.appendChild(container.childNodes[0]); + } + + this.documentId = this.xmlRoot = this.cacheId = this.$subTreeCacheContext = null; + } + else { + /* If the current item was loaded whilst offline, we won't cache + * anything + */ + if (this.$loadedWhenOffline) { + this.$loadedWhenOffline = false; + } + else { + // Here we cache the current part + var fragment = this.$getCurrentFragment(); + if (!fragment) return;//this.$setClearMessage(this["empty-message"]); + + fragment.documentId = this.documentId; + fragment.xmlRoot = this.xmlRoot; + + if (this.cacheId || this.xmlRoot) + setCache.call(this, this.cacheId || + this.xmlRoot.getAttribute(apf.xmldb.xmlIdTag) || "doc" + + this.xmlRoot.getAttribute(apf.xmldb.xmlDocTag), fragment); + } + } + }); + + /** + * Checks the cache for a cached item by ID. If the ID is found the + * representation is loaded from cache and set active. + * + * @param {String} id the id of the cache element which is looked up. + * @param {Object} xmlNode + * @return {Boolean} + * Possible values: + * true the cache element is found and set active + * false otherwise + * @see baseclass.databinding.method.load + * @private + */ + function getCache(id, xmlNode){ + /* + Let's check if the requested source is actually + a sub tree of an already rendered part + */ + + if (xmlNode && this.hasFeature(apf.__MULTISELECT__) && this.$isTreeArch) { + var cacheItem, + htmlId = xmlNode.getAttribute(apf.xmldb.xmlIdTag) + "|" + this.$uniqueId, + node = this.$pHtmlDoc.getElementById(htmlId); + if (node) + cacheItem = id ? false : this.$container; //@todo what is the purpose of this statement? + else { + for (var prop in this.cache) { + if (this.cache[prop] && this.cache[prop].nodeType) { + node = this.cache[prop].getElementById(htmlId); + if (node) { + cacheItem = id ? prop : this.cache[prop]; //@todo what is the purpose of this statement? + break; + } + } + } + } + + if (cacheItem && !this.cache[id]) { + /* + Ok so it is, let's borrow it for a while + We can't clone it, because the updates will + get ambiguous, so we have to put it back later + */ + var oHtml = this.$findHtmlNode( + xmlNode.getAttribute(apf.xmldb.xmlIdTag) + "|" + this.$uniqueId); + this.$subTreeCacheContext = { + oHtml : oHtml, + parentNode : oHtml.parentNode, + beforeNode : oHtml.nextSibling, + cacheItem : cacheItem + }; + + this.documentId = apf.xmldb.getXmlDocId(xmlNode); + this.cacheId = id; + this.xmlRoot = xmlNode; + + //Load html + if (this.renderRoot) + this.$container.appendChild(oHtml); + else { + while (oHtml.childNodes.length) + this.$container.appendChild(oHtml.childNodes[0]); + } + + return true; + } + } + + + //Checking Cache... + if (!this.cache[id]) return false; + + //Get Fragment and clear Cache Item + var fragment = this.cache[id]; + + this.documentId = fragment.documentId; + this.cacheId = id; + this.xmlRoot = xmlNode;//fragment.xmlRoot; + + + this.setProperty("root", this.xmlRoot); + + + this.clearCacheItem(id); + + this.$setCurrentFragment(fragment); + + return true; + } + + /** + * Sets cache element and it's ID + * + * @param {String} id the id of the cache element to be stored. + * @param {DocumentFragment} fragment the data to be stored. + * @private + */ + function setCache(id, fragment){ + if (!this.caching) return; + + this.cache[id] = fragment; + } + + /** + * Finds HTML presentation node in cache by ID + * + * @param {String} id the id of the HTMLElement which is looked up. + * @return {HTMLElement} the HTMLElement found. When no element is found, null is returned. + */ + this.$findHtmlNode = function(id){ + var node = this.$pHtmlDoc.getElementById(id); + if (node) return node; + + for (var prop in this.cache) { + if (this.cache[prop] && this.cache[prop].nodeType) { + node = this.cache[prop].getElementById(id); + if (node) return node; + } + } + + return null; + }; + + /** + * Removes an item from the cache. + * + * @param {String} id the id of the HTMLElement which is looked up. + * @param {Boolean} [remove] whether to destroy the Fragment. + * @see baseclass.databinding.method.clear + * @private + */ + this.clearCacheItem = function(id, remove){ + this.cache[id].documentId = + this.cache[id].cacheId = + this.cache[id].xmlRoot = null; + + if (remove) + apf.destroyHtmlNode(this.cache[id]); + + this.cache[id] = null; + }; + + /** + * Removes all items from the cache + * + * @see baseclass.databinding.method.clearCacheItem + * @private + */ + this.clearAllCache = function(){ + for (var prop in this.cache) { + if (this.cache[prop]) + this.clearCacheItem(prop, true); + } + }; + + /** + * Gets the cache item by it's id + * + * @param {String} id the id of the HTMLElement which is looked up. + * @see baseclass.databinding.method.clearCacheItem + * @private + */ + this.getCacheItem = function(id){ + return this.cache[id]; + }; + + /** + * Checks whether a cache item exists by the specified id + * + * @param {String} id the id of the cache item to check. + * @see baseclass.databinding.method.clearCacheItem + * @private + */ + this.$isCached = function(id){ + return this.cache[id] || this.cacheId == id ? true : false; + }; + + if (!this.$getCurrentFragment) { + this.$getCurrentFragment = function(){ + var fragment = this.$container.ownerDocument.createDocumentFragment(); + + while (this.$container.childNodes.length) { + fragment.appendChild(this.$container.childNodes[0]); + } + + return fragment; + }; + + this.$setCurrentFragment = function(fragment){ + this.$container.appendChild(fragment); + + if (!apf.window.hasFocus(this) && this.blur) + this.blur(); + }; + } + + /** + * @attribute {Boolean} caching whether caching is enabled for this element. + */ + this.$booleanProperties["caching"] = true; + this.$supportedProperties.push("caching"); + + this.addEventListener("DOMNodeRemovedFromDocument", function(e){ + //Remove all cached Items + this.clearAllCache(); + }); +}; + +apf.GuiElement.propHandlers["caching"] = function(value) { + if (!apf.isTrue(value)) return; + + if (!this.hasFeature(apf.__CACHE__)) + this.implement(apf.Cache); +}; + + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/core/baseclasses/rename.js)SIZE(15023)TIME(Wed, 30 Nov 2011 17:29:20 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + +apf.__RENAME__ = 1 << 10; + + + +/** + * All elements inheriting from this {@link term.baseclass baseclass} have the rename features. Rename is triggered by + * pressing F2 on an item or by clicking once on an already selected item. This + * will show an input element in place where the user can change the name of the + * item to a new one. When the caption is changed the {@link term.datanode data node} is + * changed accordingly. + * Example: + * This example shows a list containing products. Only products that have the + * editable attribute set to 1 can be renamed by the user. + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * @event stoprename Fires when a rename action is cancelled. + * + * @constructor + * @baseclass + * @author Ruben Daniels (ruben AT ajax DOT org) + * @version %I%, %G% + * @since 0.5 + */ +apf.Rename = function(){ + this.$regbase = this.$regbase|apf.__RENAME__; + + this.canrename = false; + this.$renameSubject = + this.renameTimer = + this.lastCursor = null; + + /** + * @attribute {Boolean} rename whether the user can start renaming rendered nodes in this element. + */ + this.$booleanProperties["canrename"] = true; + this.$booleanProperties["autorename"] = true; + this.$supportedProperties.push("canrename", "autorename"); + + + this.$propHandlers["autorename"] = function(value){ + if (value) { + this.reselectable = true; + this.bufferselect = false; + this.addEventListener("afterselect", $afterselect); + this.addEventListener("keydown", $keydown); + } + else { + this.removeEventListener("afterselect", $afterselect); + this.removeEventListener("keydown", $keydown); + } + } + + function $afterselect(){ + var _self = this; + $setTimeout(function(){ + if (_self.hasFocus()) + _self.startRename(); + }, 20); + } + + function $keydown(e){ + if (!this.renaming && apf.isCharacter(e.keyCode)) + this.startRename(); + } + + this.$isContentEditable = function(e){ + if (this.renaming && this.autorename) + return true; + } + + + /** + * Changes the data presented as the caption of a specified {@link term.datanode data node}. + * If none is specified the indicated node is used. + * + * @action + * @param {XMLElement} xmlNode the element to change the caption of. + * @param {String} value the value to set as the caption of the {@link term.datanode data node}. + */ + this.rename = function(xmlNode, value){ + if (!xmlNode) + xmlNode = this.caret || this.selected; + + if (!xmlNode) return; + + return this.$executeSingleValue("rename", "caption", xmlNode, value); + }; + + /** + * Starts the rename process with a delay, allowing for cancellation when + * necesary. Cancellation is necesary for instance, when double click was + * intended or a dragdrop operation. + * + */ + this.startDelayedRename = function(e, time, userAction){ + clearTimeout(this.renameTimer); + + if (e && (e.button == 2 || e.ctrlKey || e.shiftKey) + || userAction && this.disabled) + return; + + this.renameTimer = $setTimeout('apf.lookup(' + + this.$uniqueId + ').startRename()', time || 400); + }; + + /** + * Starts the rename process by displaying an input box at the position + * of the item that can be renamed by the user. + * + */ + this.startRename = function(force, startEmpty, userAction){ + if (!force && (this.renaming || !this.canrename + || !this.$startAction("rename", this.caret + || this.selected, this.stopRename)) + || userAction && this.disabled) + return false; + + if (!this.hasFocus()) + this.focus(null, null, true); + + clearTimeout(this.renameTimer); + + var elCaption = this.$getCaptionElement + ? this.$getCaptionElement() + : this.$caret || this.$selected; + + if (!elCaption) + return this.stopRename(); + + this.renaming = true; + this.$renameSubject = this.caret || this.selected; + + var wdt = elCaption.offsetWidth; + this.lastCursor = elCaption.style.cursor; + elCaption.style.cursor = "text"; + elCaption.parentNode.replaceChild(this.$txt, elCaption); + elCaption.host = this; + + if (apf.isTrue(this.$getOption("main", "scalerename"))) { + var diff = apf.getWidthDiff(this.$txt); + this.$txt.style.width = (wdt - diff - 3) + "px"; + } + + this.$replacedNode = elCaption; + var xmlNode = this.$getCaptionXml + ? this.$getCaptionXml(this.$renameSubject) + : this.$getDataNode("caption", this.$renameSubject); + + //xmlNode.nodeType >= 2 && xmlNode.nodeType <= 4 + var value = startEmpty || !xmlNode + ? "" + : (xmlNode.nodeType != 1 + ? unescape(xmlNode.nodeValue) //decodeURI( - throws an error when using % in a non expected way + : (apf.isOnlyChild(xmlNode.firstChild, [3,4]) + ? apf.queryValue(xmlNode) + : this.$applyBindRule("caption", this.$renameSubject))) || ""; + + if (apf.hasContentEditable) { + if (this.$multiLineRename) + this.$txt.innerHTML = apf.htmlCleaner.prepare(value.trim() + .replace(//g, ">") + .replace(/\n/g, "
")); + else + this.$txt.innerHTML = value.replace(/" || ""; + } + else + this.$txt.value = value; + + this.$txt.unselectable = "Off"; + this.$txt.host = this; + + //this.$txt.focus(); + var txt = this.$txt; + var f = function(){ + try { + txt.focus(); + txt.select(); + } + catch(e) {} + }; + if (apf.isIE) f() + else $setTimeout(f); + }; + + /** + * Stop renaming process and change the data according to the set value. + * Cancel the renaming process without changing data. + * + */ + this.stopRename = function(contextXml, success){ + clearTimeout(this.renameTimer); + + if (!this.renaming || contextXml && contextXml != this.$renameSubject + || !this.$replacedNode) + return false; + + this.renaming = false; + + if (this.$txt.parentNode && this.$txt.parentNode.nodeType == 1) { + if (apf.isIE8 || apf.isIE7Emulate) + this.$txt.blur(); + + this.$txt.parentNode.replaceChild(this.$replacedNode, this.$txt); + } + + if (this.$replacedNode) { + this.$replacedNode.style.cursor = this.lastCursor || ""; + this.$replacedNode.host = null; + } + + //apf.hasContentEditable ?? + if (this.$multiLineRename) { + var value = apf.html_entity_decode( + apf.htmlCleaner.parse(this.$txt.innerHTML, true) + .replace(/
/g, "") + .replace(/<\/?p>/g, "")); + } + else { + var value = this.$txt[apf.hasContentEditable ? "innerText" : "value"] + .replace(/<.*?nobr>/gi, "").replace(/\n$/, ""); //last replace is for chrome + } + + if (!success || (this.$validateRename && !this.$validateRename(value))) { + this.dispatchEvent("stoprename"); + this.$stopAction("rename"); + } + else { + //this.$selected.innerHTML = this.$txt.innerHTML; + if (this.rename(this.$renameSubject, value) !== false) { + if (this.$replacedNode) + this.$replacedNode.innerHTML = value.replace(/"); + } + } + + if (!this.renaming) { + this.$renameSubject = null; + this.$replacedNode = null; + this.$txt.style.width = ""; + } + + return true; + }; + + + this.addEventListener("keydown", function(e){ + var key = e.keyCode; + + if (this.renaming) { + if (key == 27 || this.$multiLineRename && e.ctrlKey && key == 13 + || !this.$multiLineRename && key == 13) { + this.stopRename(null, key == 13 && !this.$autocomplete); + e.cancelBubble = true; + return false; + } + else if (apf.hasContentEditableContainerBug && key == 8 + && this.$txt.innerHTML == "
") { + e.preventDefault(); + } + + return; + } + + //F2 + if (key == 113) { + if (this.$tempsel) + this.$selectTemp(); + + if (this.caret != this.selected) { + if (this.multiselect || this.isSelected(this.caret)) { + this.selected = this.caret; + this.$selected = this.$caret; + } + else + this.select(this.caret, true); + } + + this.startRename(); + + return false; + } + }, true); + + + this.addEventListener("DOMNodeRemovedFromDocument", function(e){ + this.$txt.refCount--; + + if (!this.$txt.refCount) { + this.$txt.host = + this.$txt.onmouseover = + this.$txt.onmousedown = + this.$txt.select = + this.$txt.onfocus = + this.$txt.onblur = null; + } + this.$txt = null; + }); + + this.$init(apf.Rename.initEditableArea); +}; + +apf.Rename.initEditableArea = function(){ + if (!(this.$txt = document.getElementById("txt_rename"))) { + if (apf.hasContentEditable) { + this.$txt = document.createElement("DIV"); + this.$txt.contentEditable = true; + if (apf.isIE6) + this.$txt.style.width = "1px"; + //this.$txt.canHaveHTML = false; + } + else { + this.$txt = document.createElement("input"); + this.$txt.id = "txt_rename"; + this.$txt.autocomplete = false; + } + + + //if (apf.hasFocusBug) + //apf.sanitizeTextbox(this.$txt); + + + this.$txt.refCount = 0; + this.$txt.id = "txt_rename"; + //this.$txt.style.whiteSpace = "nowrap"; + apf.importCssString("#txt_rename{white-space:nowrap}"); + this.$txt.onselectstart = function(e){ + (e || event).cancelBubble = true; + }; + + this.$txt.onmouseover = + this.$txt.onmouseout = + this.$txt.oncontextmenu = + //this.$txt.onkeydown = + this.$txt.onmouseup = + this.$txt.ondblclick = + this.$txt.onmousedown = function(e){ + apf.stopPropagation(e || event) + }; + + this.$txt.onkeyup = function(e){ + //(e || event).cancelBubble = true; + + if (!this.host.$autocomplete) + return; + + this.host.$lookup(this[apf.hasContentEditable ? "innerHTML" : "value"]); + } + + var sel; + this.$txt.select = function(){ + if (!apf.hasMsRangeObject) { + if (window.getSelection && document.createRange) { + var sel = window.getSelection(); + sel.removeAllRanges(); + var r = document.createRange(); + r.setStart(this.firstChild, 0); + var lastIndex = this.firstChild.nodeValue.lastIndexOf("."); + r.setEnd(this.firstChild, lastIndex > -1 ? lastIndex : this.firstChild.nodeValue.length); + sel.addRange(r) + } + else { + (sel || (sel = new apf.selection())).selectNode(this); + } + return; + } + + var r = document.selection.createRange(); + //r.moveEnd("character", this.$ext.innerText.length); + try { + r.moveToElementText(this); + + if (apf.isFalse(this.host.$getOption("main", "selectrename")) + || typeof this.host.$renameStartCollapse != "undefined") //@todo please deprecate renameStartCollapse + r.collapse(this.host.$renameStartCollapse); + } catch(e) {} //BUG!!!! + + r.select(); + }; + + + if (apf.hasFocusBug) { + this.$txt.onfocus = function(){ + if (apf.window) + apf.window.$focusfix2(); + }; + } + + + this.$txt.onblur = function(){ + //if (apf.isGecko) + //return; //bug in firefox calling onblur too much + //if (apf.isChrome && !arguments.callee.caller) + //return; + + + if (apf.hasFocusBug) + apf.window.$blurfix(); + + + if (this.host.$autocomplete) + return; + + this.host.stopRename(null, true); + }; + } + + this.$txt.refCount++; +} + + + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/core/baseclasses/a11y.js)SIZE(5144)TIME(Fri, 14 Oct 2011 08:01:44 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + +apf.__ALIGNMENT__ = 1 << 29; + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/core/baseclasses/basebutton.js)SIZE(10335)TIME(Fri, 14 Oct 2011 08:01:44 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + + +/** + * Baseclass of an element that has one or two states and can be clicked on to + * trigger an action. (i.e. {@link element.button} or {@link element.checkbox}). + * + * @constructor + * @baseclass + * @author Abe Ginner + * @version %I%, %G% + * @since 0.8 + * + * @event click Fires when the user presses a mousebutton while over this element and then let's the mousebutton go. + */ +apf.BaseButton = function(){ + this.$init(true); +}; + +(function() { + + this.implement(apf.ChildValue); + + + this.$refKeyDown = // Number of keys pressed. + this.$refMouseDown = 0; // Mouse button down? + this.$mouseOver = // Mouse hovering over the button? + this.$mouseLeft = false; // Has the mouse left the control since pressing the button. + + /**** Properties and Attributes ****/ + + /** + * @attribute {string} background sets a multistate background. The arguments + * are seperated by pipes '|' and are in the order of: + * 'imagefilename|mapdirection|nrofstates|imagesize' + * The mapdirection argument may have the value of 'vertical' or 'horizontal'. + * The nrofstates argument specifies the number of states the iconfile contains: + * 1 - normal + * 2 - normal, hover + * 3 - normal, hover, down + * 4 - normal, hover, down, disabled + * The imagesize argument specifies how high or wide each icon is inside the + * map, depending of the mapdirection argument. + * + * Example: + * A 3 state picture where each state is 16px high, vertically spaced + * + * background="threestates.gif|vertical|3|16" + * + */ + this.$propHandlers["background"] = function(value){ + var oNode = this.$getLayoutNode("main", "background", this.$ext); + + if (!oNode) return; + + + if (value) { + var b = value.split("|"); + this.$background = b.concat(["vertical", 2, 16].slice(b.length - 1)); + + oNode.style.backgroundImage = "url(" + this.mediaPath + b[0] + ")"; + oNode.style.backgroundRepeat = "no-repeat"; + } + else { + oNode.style.backgroundImage = ""; + oNode.style.backgroundRepeat = ""; + this.$background = null; + } + }; + + /**** Keyboard Support ****/ + + + this.addEventListener("keydown", function(e){ + var key = e.keyCode; + //var ctrlKey = e.ctrlKey; << UNUSED + //var shiftKey = e.shiftKey; << UNUSED + + switch (key) { + case 13: + if (this.localName != "checkbox") + this.$ext.onmouseup(e.htmlEvent, true); + break; + case 32: + if (!e.htmlEvent.repeat) { // Only when first pressed, not on autorepeat. + this.$refKeyDown++; + this.$updateState(e.htmlEvent); + } + return false; + } + }, true); + + this.addEventListener("keyup", function(e){ + var key = e.keyCode; + + switch (key) { + case 32: + this.$refKeyDown--; + + if (this.$refKeyDown < 0) { + this.$refKeyDown = 0; + return false; + } + + if (this.$refKeyDown + this.$refMouseDown == 0 && !this.disabled) + this.$ext.onmouseup(e, true); + + this.$updateState(e); + return false; + } + }, true); + + + /**** Private state handling methods ****/ + + this.states = { + "Out" : 1, + "Over" : 2, + "Down" : 3 + }; + + this.$updateState = function(e, strEvent) { + if (e.reset) { //this.disabled || + this.$refKeyDown = 0; + this.$refMouseDown = 0; + this.$mouseOver = false; + return false; + } + + if (this.$refKeyDown > 0 + || (this.$refMouseDown > 0 && (this.$mouseOver || (!apf.isIE && this.$ext === e.currentTarget))) + || (this.isBoolean && this.value)) { + this.$setState("Down", e, strEvent); + } + else if (this.$mouseOver) { + this.$setState("Over", e, strEvent); + } + else + this.$setState("Out", e, strEvent); + }; + + this.$setupEvents = function() { + if (this.editable) + return; + + var _self = this; + + this.$ext.onmousedown = function(e) { + e = e || window.event; + + if (_self.$notfromext && (e.srcElement || e.target) == this) + return; + + _self.$refMouseDown = 1; + _self.$mouseLeft = false; + + if (_self.disabled) + return; + + if (!apf.isIE) { // && (apf.isGecko || !_self.submenu) Causes a focus problem for menus + if (_self.value) + apf.stopEvent(e); + else + apf.cancelBubble(e); + } + + _self.$updateState(e, "mousedown"); + }; + + this.$ext.onmouseup = function(e, force) { + e = e || window.event; + //if (e) e.cancelBubble = true; + if (_self.disabled || !force && ((!_self.$mouseOver && (!apf.isIE && this !== e.currentTarget)) || !_self.$refMouseDown)) + return; + + _self.$refMouseDown = 0; + _self.$updateState(e, "mouseup"); + + // If this is coming from a mouse click, we shouldn't have left the button. + if (_self.disabled || (e && e.type == "click" && _self.$mouseLeft == true)) + return false; + + // If there are still buttons down, this is not a real click. + if (_self.$refMouseDown + _self.$refKeyDown) + return false; + + if (_self.$clickHandler && _self.$clickHandler()) + _self.$updateState (e || event, "click"); + else + _self.dispatchEvent("click", {htmlEvent : e}); + + return false; + }; + + this.$ext.onmousemove = function(e) { + if ((!_self.$mouseOver || _self.$mouseOver == 2)) { + e = e || window.event; + + if (_self.$notfromext && (e.srcElement || e.target) == this) + return; + + _self.$mouseOver = true; + + if (!_self.disabled) + _self.$updateState(e, "mouseover"); + } + }; + + this.$ext.onmouseout = function(e) { + e = e || window.event; + + //Check if the mouse out is meant for us + var tEl = e.explicitOriginalTarget || e.toElement; + if (apf.isChildOf(this, tEl)) //this == tEl || + return; + + _self.$mouseOver = false; + _self.$refMouseDown = 0; + _self.$mouseLeft = true; + + if (!_self.disabled) + _self.$updateState(e, "mouseout"); + }; + + + + if (apf.hasClickFastBug) + this.$ext.ondblclick = this.$ext.onmouseup; + }; + + this.$doBgSwitch = function(nr){ + if (this.background && (this.$background[2] >= nr || nr == 4)) { + if (nr == 4) + nr = this.$background[2] + 1; + + var strBG = this.$background[1] == "vertical" + ? "0 -" + (parseInt(this.$background[3]) * (nr - 1)) + "px" + : "-" + (parseInt(this.$background[3]) * (nr - 1)) + "px 0"; + + this.$getLayoutNode("main", "background", + this.$ext).style.backgroundPosition = strBG; + } + }; + + /**** Focus Handling ****/ + + this.$focus = function(){ + if (!this.$ext) + return; + + this.$setStyleClass(this.$ext, this.$baseCSSname + "Focus"); + }; + + this.$blur = function(e){ + if (!this.$ext) + return; //FIREFOX BUG! + + this.$setStyleClass(this.$ext, "", [this.$baseCSSname + "Focus"]); + /*this.$refKeyDown = 0; + this.$refMouseDown = 0; + this.$mouseLeft = true;*/ + + + /*if (this.submenu) { + if (this.value) { + this.$setState("Down", {}, "mousedown"); + this.$hideMenu(); + } + }*/ + + + if (e) + this.$updateState({});//, "onblur" + }; + + this.addEventListener("prop.disabled", function(e){ + this.$refKeyDown = + this.$refMouseDown = 0; + //this.$mouseOver = + //this.$mouseLeft = false; + }); + + /*** Clearing potential memory leaks ****/ + + this.$destroy = function(skinChange){ + if (!skinChange && this.$ext) { + this.$ext.onmousedown = this.$ext.onmouseup = this.$ext.onmouseover = + this.$ext.onmouseout = this.$ext.onclick = this.$ext.ondblclick = null; + } + }; + +}).call(apf.BaseButton.prototype = new apf.StandardBinding()); + + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/core/baseclasses/baselist.js)SIZE(38108)TIME(Fri, 14 Oct 2011 08:01:44 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + + +/** + * Baseclass of elements that allows the user to select one or more items + * out of a list. + * + * @constructor + * @baseclass + * + * @inherits apf.MultiSelect + * @inherits apf.Cache + * @inherits apf.DataAction + * @inherits apf.XForms + * + * @author Ruben Daniels (ruben AT ajax DOT org) + * @version %I%, %G% + * @since 0.8 + * @default_private + * + * @binding caption Determines the caption of a node. + * @binding icon Determines the icon of a node. This binding rule is used + * to determine the icon displayed when using a list skin. The {@link baseclass.baselist.binding.image image binding} + * is used to determine the image in the thumbnail skin. + * @binding image Determines the image of a node. This binding rule is used + * to determine the image displayed when using a thumbnail skin. The {@link baseclass.baselist.binding.icon icon binding} + * is used to determine the icon in the list skin. + * Example: + * In this example the image url is read from the thumbnail attribute of the data node. + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * @binding css Determines a css class for a node. + * Example: + * In this example a node is bold when the folder contains unread messages: + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * @binding tooltip Determines the tooltip of a node. + * @event notunique Fires when the more attribute is set and an item is added that has a caption that already exists in the list. + * object: + * {String} value the value that was entered. + */ +apf.BaseList = function(){ + this.$init(true); + + + this.$dynCssClasses = []; + + + this.listNodes = []; +}; + +(function() { + + this.implement( + + apf.Cache, + + + apf.DataAction, + + + apf.K + ); + + + /**** Properties and Attributes ****/ + + this.$focussable = true; // This object can get the focus + this.$isWindowContainer = -1; + + this.multiselect = true; // Initially Disable MultiSelect + + /** + * @attribute {String} fill the set of items that should be loaded into this + * element. A start and an end seperated by a -. + * Example: + * This example loads a list with items starting at 1980 and ending at 2050. + * + * + * + * + * + * + * + */ + this.$propHandlers["fill"] = function(value){ + if (value) + this.loadFillData(this.getAttribute("fill")); + else + this.clear(); + }; + + + + /** + * @attribute {String} mode Sets the way this element interacts with the user. + * Possible values: + * check the user can select a single item from this element. The selected item is indicated. + * radio the user can select multiple items from this element. Each selected item is indicated. + */ + this.$mode = 0; + this.$propHandlers["mode"] = function(value){ + if ("check|radio".indexOf(value) > -1) { + if (!this.hasFeature(apf.__MULTICHECK__)) + this.implement(apf.MultiCheck); + + this.addEventListener("afterrename", $afterRenameMode); //what does this do? + + this.multicheck = value == "check"; //radio is single + this.$mode = this.multicheck ? 1 : 2; + } + else { + //@todo undo actionRules setting + this.removeEventListener("afterrename", $afterRenameMode); + //@todo unimplement?? + this.$mode = 0; + } + }; + + //@todo apf3.0 retest this completely + function $afterRenameMode(){ + } + + + + /**** Keyboard support ****/ + + + + //Handler for a plane list + this.$keyHandler = function(e){ + var key = e.keyCode, + ctrlKey = e.ctrlKey, + shiftKey = e.shiftKey, + selHtml = this.$caret || this.$selected; + + if (e.returnValue == -1 || !selHtml || this.renaming) //@todo how about allowdeselect? + return; + + var selXml = this.caret || this.selected, + oExt = this.$ext, + // variables used in the switch statement below: + node, margin, items, lines, hasScroll, hasScrollX, hasScrollY; + + switch (key) { + case 13: + if (this.$tempsel) + this.$selectTemp(); + + if (this.ctrlselect == "enter") + this.select(this.caret, true); + + this.choose(this.selected); + break; + case 32: + if (ctrlKey || !this.isSelected(this.caret)) + this.select(this.caret, ctrlKey); + break; + case 109: + case 46: + //DELETE + if (this.disableremove) + return; + + if (this.$tempsel) + this.$selectTemp(); + + this.remove(); + break; + case 36: + //HOME + if (this.hasFeature(apf.__VIRTUALVIEWPORT__)) + this.viewport.change(0, null, true, true); + + this.select(this.getFirstTraverseNode(), false, shiftKey); + this.$container.scrollTop = 0; + break; + case 35: + //END + if (this.hasFeature(apf.__VIRTUALVIEWPORT__)) + this.viewport.change(this.viewport.length, null, true, true); + + this.select(this.getLastTraverseNode(), false, shiftKey); + this.$container.scrollTop = this.$container.scrollHeight; + break; + case 107: + //+ + if (this.more) + this.startMore(); + break; + case 37: + //LEFT + if (!selXml && !this.$tempsel) + return; + + node = this.$tempsel + ? apf.xmldb.getNode(this.$tempsel) + : selXml; + margin = apf.getBox(apf.getStyle(selHtml, "margin")); + items = selHtml.offsetWidth + ? Math.floor((oExt.offsetWidth + - (hasScroll ? 15 : 0)) / (selHtml.offsetWidth + + margin[1] + margin[3])) + : 1; + + //margin = apf.getBox(apf.getStyle(selHtml, "margin")); + + node = this.getNextTraverseSelected(node, false); + if (node) + this.$setTempSelected(node, ctrlKey, shiftKey, true); + else + return; + + selHtml = apf.xmldb.findHtmlNode(node, this); + if (selHtml.offsetTop < oExt.scrollTop) { + oExt.scrollTop = Array.prototype.indexOf.call(this.getTraverseNodes(), node) < items + ? 0 + : selHtml.offsetTop - margin[0]; + } + break; + case 38: + //UP + if (!selXml && !this.$tempsel) + return; + + node = this.$tempsel + ? apf.xmldb.getNode(this.$tempsel) + : selXml; + + margin = apf.getBox(apf.getStyle(selHtml, "margin")); + hasScroll = oExt.scrollHeight > oExt.offsetHeight; + items = selHtml.offsetWidth + ? Math.floor((oExt.offsetWidth + - (hasScroll ? 15 : 0)) / (selHtml.offsetWidth + + margin[1] + margin[3])) + : 1; + + node = this.getNextTraverseSelected(node, false, items); + if (node) + this.$setTempSelected (node, ctrlKey, shiftKey, true); + else + return; + + selHtml = apf.xmldb.findHtmlNode(node, this); + if (selHtml.offsetTop < oExt.scrollTop) { + oExt.scrollTop = Array.prototype.indexOf.call(this.getTraverseNodes(), node) < items + ? 0 + : selHtml.offsetTop - margin[0]; + } + break; + case 39: + //RIGHT + if (!selXml && !this.$tempsel) + return; + + node = this.$tempsel + ? apf.xmldb.getNode(this.$tempsel) + : selXml; + margin = apf.getBox(apf.getStyle(selHtml, "margin")); + node = this.getNextTraverseSelected(node, true); + if (node) + this.$setTempSelected (node, ctrlKey, shiftKey); + else + return; + + selHtml = apf.xmldb.findHtmlNode(node, this); + if (selHtml.offsetTop + selHtml.offsetHeight + > oExt.scrollTop + oExt.offsetHeight) { + oExt.scrollTop = selHtml.offsetTop + - oExt.offsetHeight + selHtml.offsetHeight + + margin[0]; + } + break; + case 40: + //DOWN + if (!selXml && !this.$tempsel) + return; + + node = this.$tempsel + ? apf.xmldb.getNode(this.$tempsel) + : selXml; + + margin = apf.getBox(apf.getStyle(selHtml, "margin")); + hasScroll = oExt.scrollHeight > oExt.offsetHeight; + items = selHtml.offsetWidth + ? Math.floor((oExt.offsetWidth + - (hasScroll ? 15 : 0)) / (selHtml.offsetWidth + + margin[1] + margin[3])) + : 1; + + node = this.getNextTraverseSelected(node, true, items); + if (node) + this.$setTempSelected (node, ctrlKey, shiftKey); + else + return; + + selHtml = apf.xmldb.findHtmlNode(node, this); + if (selHtml.offsetTop + selHtml.offsetHeight + > oExt.scrollTop + oExt.offsetHeight) { // - (hasScroll ? 10 : 0) + oExt.scrollTop = selHtml.offsetTop + - oExt.offsetHeight + selHtml.offsetHeight + + margin[0]; //+ (hasScroll ? 10 : 0) + } + break; + case 33: + //PGUP + if (!selXml && !this.$tempsel) + return; + + node = this.$tempsel + ? apf.xmldb.getNode(this.$tempsel) + : selXml; + + margin = apf.getBox(apf.getStyle(selHtml, "margin")); + hasScrollY = oExt.scrollHeight > oExt.offsetHeight; + hasScrollX = oExt.scrollWidth > oExt.offsetWidth; + items = Math.floor((oExt.offsetWidth + - (hasScrollY ? 15 : 0)) / (selHtml.offsetWidth + + margin[1] + margin[3])); + lines = Math.floor((oExt.offsetHeight + - (hasScrollX ? 15 : 0)) / (selHtml.offsetHeight + + margin[0] + margin[2])); + + node = this.getNextTraverseSelected(node, false, items * lines); + if (!node) + node = this.getFirstTraverseNode(); + if (node) + this.$setTempSelected (node, ctrlKey, shiftKey, true); + else + return; + + selHtml = apf.xmldb.findHtmlNode(node, this); + if (selHtml.offsetTop < oExt.scrollTop) { + oExt.scrollTop = Array.prototype.indexOf.call(this.getTraverseNodes(), node) < items + ? 0 + : selHtml.offsetTop - margin[0]; + } + break; + case 34: + //PGDN + if (!selXml && !this.$tempsel) + return; + + node = this.$tempsel + ? apf.xmldb.getNode(this.$tempsel) + : selXml; + + margin = apf.getBox(apf.getStyle(selHtml, "margin")); + hasScrollY = oExt.scrollHeight > oExt.offsetHeight; + hasScrollX = oExt.scrollWidth > oExt.offsetWidth; + items = Math.floor((oExt.offsetWidth - (hasScrollY ? 15 : 0)) + / (selHtml.offsetWidth + margin[1] + margin[3])); + lines = Math.floor((oExt.offsetHeight - (hasScrollX ? 15 : 0)) + / (selHtml.offsetHeight + margin[0] + margin[2])); + + node = this.getNextTraverseSelected(selXml, true, items * lines); + if (!node) + node = this.getLastTraverseNode(); + if (node) + this.$setTempSelected (node, ctrlKey, shiftKey); + else + return; + + selHtml = apf.xmldb.findHtmlNode(node, this); + if (selHtml.offsetTop + selHtml.offsetHeight + > oExt.scrollTop + oExt.offsetHeight) { // - (hasScrollY ? 10 : 0) + oExt.scrollTop = selHtml.offsetTop + - oExt.offsetHeight + selHtml.offsetHeight + + margin[0]; //+ 10 + (hasScrollY ? 10 : 0) + } + break; + + default: + if (key == 65 && ctrlKey) { + this.selectAll(); + } + else if (this.$hasBindRule("caption")) { + if (!this.xmlRoot || this.autorename) return; + + //this should move to a onkeypress based function + if (!this.lookup || new Date().getTime() + - this.lookup.date.getTime() > 300) { + this.lookup = { + str : "", + date : new Date() + }; + } + + this.lookup.str += String.fromCharCode(key); + + var nodes = this.getTraverseNodes(); //@todo start at current indicator + for (var v, i = 0; i < nodes.length; i++) { + v = this.$applyBindRule("caption", nodes[i]); + if (v && v.substr(0, this.lookup.str.length) + .toUpperCase() == this.lookup.str) { + + if (!this.isSelected(nodes[i])) { + this.select(nodes[i]); + } + + if (selHtml) { + this.$container.scrollTop = selHtml.offsetTop + - (this.$container.offsetHeight + - selHtml.offsetHeight) / 2; + } + return; + } + } + return; + } + break; + } + + this.lookup = null; + return false; + }; + + + + /**** Private databinding functions ****/ + + this.$deInitNode = function(xmlNode, htmlNode){ + if (!htmlNode) return; + + //Remove htmlNodes from tree + htmlNode.parentNode.removeChild(htmlNode); + }; + + this.$updateNode = function(xmlNode, htmlNode, noModifier){ + //Update Identity (Look) + var elIcon = this.$getLayoutNode("item", "icon", htmlNode); + + if (elIcon) { + if (elIcon.nodeType == 1) { + elIcon.style.backgroundImage = "url(" + + apf.getAbsolutePath(this.iconPath, + this.$applyBindRule("icon", xmlNode)) + ")"; + } + else { + elIcon.nodeValue = apf.getAbsolutePath(this.iconPath, + this.$applyBindRule("icon", xmlNode)); + } + } + else { + //.style.backgroundImage = "url(" + this.$applyBindRule("image", xmlNode) + ")"; + var elImage = this.$getLayoutNode("item", "image", htmlNode); + if (elImage) { + if (elImage.nodeType == 1) { + elImage.style.backgroundImage = "url(" + + apf.getAbsolutePath(apf.hostPath, + this.$applyBindRule("image", xmlNode)) + ")"; + } + else { + elImage.nodeValue = apf.getAbsolutePath(apf.hostPath, + this.$applyBindRule("image", xmlNode)); + } + } + } + + var elCaption = this.$getLayoutNode("item", "caption", htmlNode); + if (elCaption) { + if (elCaption.nodeType == 1) { + + if (!this.$cbindings.caption || !this.$cbindings.caption.hasAml) + + elCaption.innerHTML = this.$applyBindRule("caption", xmlNode); + } + else + elCaption.nodeValue = this.$applyBindRule("caption", xmlNode); + } + + + //@todo + + + htmlNode.title = this.$applyBindRule("title", xmlNode) || ""; + + + var cssClass = this.$applyBindRule("css", xmlNode); + + if (cssClass || this.$dynCssClasses.length) { + this.$setStyleClass(htmlNode, cssClass, this.$dynCssClasses); + if (cssClass && !this.$dynCssClasses.contains(cssClass)) { + this.$dynCssClasses.push(cssClass); + } + } + + + if (!noModifier && this.$updateModifier) + this.$updateModifier(xmlNode, htmlNode); + }; + + this.$moveNode = function(xmlNode, htmlNode){ + if (!htmlNode) return; + + var oPHtmlNode = htmlNode.parentNode; + var nNode = this.getNextTraverse(xmlNode); //@todo could optimize because getTraverseNodes returns array indexOf + var beforeNode = nNode + ? apf.xmldb.findHtmlNode(nNode, this) + : null; + + oPHtmlNode.insertBefore(htmlNode, beforeNode); + //if(this.emptyMessage && !oPHtmlNode.childNodes.length) this.setEmpty(oPHtmlNode); + }; + + this.$add = function(xmlNode, Lid, xmlParentNode, htmlParentNode, beforeNode){ + //Build Row + this.$getNewContext("item"); + var oItem = this.$getLayoutNode("item"), + elSelect = this.$getLayoutNode("item", "select"), + elIcon = this.$getLayoutNode("item", "icon"), + elImage = this.$getLayoutNode("item", "image"), + //elCheckbox = this.$getLayoutNode("item", "checkbox"), // NOT USED + elCaption = this.$getLayoutNode("item", "caption"); + + oItem.setAttribute("id", Lid); + + elSelect.setAttribute("onmouseover", "var o = apf.lookup(" + this.$uniqueId + + "); o.$setStyleClass(this, 'hover', null, true);"); + elSelect.setAttribute("onselectstart", "return false;"); + elSelect.setAttribute("style", (elSelect.getAttribute("style") || "") + + ";user-select:none;-moz-user-select:none;-webkit-user-select:none;"); + + if (this.hasFeature(apf.__RENAME__) || this.hasFeature(apf.__DRAGDROP__)) { + elSelect.setAttribute("ondblclick", "var o = apf.lookup(" + this.$uniqueId + "); " + + + "o.stopRename();" + + + " o.choose()"); + elSelect.setAttribute("onmouseout", "var o = apf.lookup(" + this.$uniqueId + ");\ + o.$setStyleClass(this, '', ['hover'], true);\ + this.hasPassedDown = false;"); + elSelect.setAttribute(this.itemSelectEvent || "onmousedown", + 'var o = apf.lookup(' + this.$uniqueId + ');\ + var xmlNode = apf.xmldb.findXmlNode(this);\ + var isSelected = o.isSelected(xmlNode);\ + this.hasPassedDown = true;\ + if (event.button == 2) \ + o.stopRename();\ + else if (!o.renaming && o.hasFocus() && isSelected == 1) \ + this.dorename = true;\ + if (!o.hasFeature(apf.__DRAGDROP__) || !isSelected && !event.ctrlKey)\ + o.select(this, event.ctrlKey, event.shiftKey, -1)'); + elSelect.setAttribute("onmouseup", 'if (!this.hasPassedDown) return;\ + var o = apf.lookup(' + this.$uniqueId + ');' + + + 'if (o.hasFeature(apf.__RENAME__) && this.dorename)\ + o.startDelayedRename(event, null, true);' + + + 'this.dorename = false;\ + var xmlNode = apf.xmldb.findXmlNode(this);\ + var isSelected = o.isSelected(xmlNode);\ + if (o.hasFeature(apf.__DRAGDROP__))\ + o.select(this, event.ctrlKey, event.shiftKey, -1)'); + } //@todo add DRAGDROP ifdefs + else { + elSelect.setAttribute("onmouseout", "apf.setStyleClass(this, '', ['hover']);"); + elSelect.setAttribute("ondblclick", 'var o = apf.lookup(' + + this.$uniqueId + '); o.choose(null, true)'); + elSelect.setAttribute(this.itemSelectEvent + || "onmousedown", 'var o = apf.lookup(' + this.$uniqueId + + '); o.select(this, event.ctrlKey, event.shiftKey, -1)'); + } + + + + + if (this.$mode) { + var elCheck = this.$getLayoutNode("item", "check"); + if (elCheck) { + elCheck.setAttribute("onmousedown", + "var o = apf.lookup(" + this.$uniqueId + ");\ + o.checkToggle(this, true);\o.$skipSelect = true;"); + + if (apf.isTrue(this.$applyBindRule("checked", xmlNode))) { + this.$checkedList.push(xmlNode); + this.$setStyleClass(oItem, "checked"); + } + else if (this.isChecked(xmlNode)) + this.$setStyleClass(oItem, "checked"); + } + else { + + return false; + } + } + + + //Setup Nodes Identity (Look) + if (elIcon) { + if (elIcon.nodeType == 1) { + elIcon.setAttribute("style", "background-image:url(" + + apf.getAbsolutePath(this.iconPath, this.$applyBindRule("icon", xmlNode)) + + ")"); + } + else { + elIcon.nodeValue = apf.getAbsolutePath(this.iconPath, + this.$applyBindRule("icon", xmlNode)); + } + } + else if (elImage) { + if (elImage.nodeType == 1) { + if ((elImage.tagName || "").toLowerCase() == "img") { + elImage.setAttribute("src", apf.getAbsolutePath(apf.hostPath, this.$applyBindRule("image", xmlNode))); + } + else { + elImage.setAttribute("style", "background-image:url(" + + apf.getAbsolutePath(apf.hostPath, this.$applyBindRule("image", xmlNode)) + + ")"); + } + } + else { + if (apf.isSafariOld) { //@todo this should be changed... blrgh.. + var p = elImage.ownerElement.parentNode, + img = p.appendChild(p.ownerDocument.createElement("img")); + img.setAttribute("src", + apf.getAbsolutePath(apf.hostPath, this.$applyBindRule("image", xmlNode))); + } + else { + elImage.nodeValue = + apf.getAbsolutePath(apf.hostPath, this.$applyBindRule("image", xmlNode)); + } + } + } + + if (elCaption) { + + if (elCaption.nodeType == 1 + && this.$cbindings.caption && this.$cbindings.caption.hasAml){ + var q = (this.$amlBindQueue || (this.$amlBindQueue = {})); + + if (elCaption == oItem) { + apf.setNodeValue(elCaption, ""); + var span = elCaption.appendChild(elCaption.ownerDocument.createElement("span")); + if (apf.isIE) + span.appendChild(elCaption.ownerDocument.createTextNode(" ")); + span.setAttribute("id", "placeholder_" + this.$uniqueId + + "_" + ((q.caption || (q.caption = [])).push(xmlNode) - 1)); + } + else { + elCaption.setAttribute("id", "placeholder_" + this.$uniqueId + + "_" + ((q.caption || (q.caption = [])).push(xmlNode) - 1)); + apf.setNodeValue(elCaption, ""); + } + } + else + + { + apf.setNodeValue(elCaption, + this.$applyBindRule("caption", xmlNode)); + } + } + oItem.setAttribute("title", this.$applyBindRule("tooltip", xmlNode) || ""); + + + var cssClass = this.$applyBindRule("css", xmlNode); + if (cssClass) { + this.$setStyleClass(oItem, cssClass); + if (cssClass) + this.$dynCssClasses.push(cssClass); + } + + + if (this.$addModifier && + this.$addModifier(xmlNode, oItem, htmlParentNode, beforeNode) === false) + return; + + if (htmlParentNode) + apf.insertHtmlNode(oItem, htmlParentNode, beforeNode); + else + this.listNodes.push(oItem); + }; + + this.addEventListener("$skinchange", function(e){ + if (this.more) + delete this.moreItem; + }); + + this.$fill = function(){ + if (this.more && !this.moreItem) { + this.$getNewContext("item"); + var Item = this.$getLayoutNode("item"), + elCaption = this.$getLayoutNode("item", "caption"), + elSelect = this.$getLayoutNode("item", "select"); + + Item.setAttribute("class", this.$baseCSSname + "More"); + elSelect.setAttribute("onmousedown", 'var o = apf.lookup(' + this.$uniqueId + + ');o.clearSelection();o.$setStyleClass(this, "more_down", null, true);'); + elSelect.setAttribute("onmouseout", 'apf.lookup(' + this.$uniqueId + + ').$setStyleClass(this, "", ["more_down"], true);'); + elSelect.setAttribute("onmouseup", 'apf.lookup(' + this.$uniqueId + + ').startMore(this, true)'); + + if (elCaption) + apf.setNodeValue(elCaption, + this.more.match(/caption:(.*)(;|$)/i)[1]); + this.listNodes.push(Item); + } + + apf.insertHtmlNodes(this.listNodes, this.$container); + this.listNodes.length = 0; + + if (this.more && !this.moreItem) { + this.moreItem = this.$container.lastChild; + } + + }; + + /** + * Adds a new item to the list and lets the users type in the new name. + * This functionality is especially useful in the interface when + * {@link element.list.attribute.mode} is set to check or radio. For instance in a form. + * @see element.list.attribute.more + */ + this.startMore = function(o, userAction){ + if (userAction && this.disabled) + return; + + this.$setStyleClass(o, "", ["more_down"]); + + var xmlNode; + if (!this.$actions["add"]) { + if (this.each && !this.each.match(/[\/\[]/)) { + xmlNode = "<" + this.each + (this.each.match(/^a:/) + ? " xmlns:a='" + apf.ns.aml + "'" + : "") + " custom='1' />"; + } + else { + + //return false; + xmlNode = ""; + } + } + + this.add(xmlNode, null, null, function(addedNode){ + this.select(addedNode, null, null, null, null, true); + if (this.morePos == "begin") + this.$container.insertBefore(this.moreItem, this.$container.firstChild); + else + this.$container.appendChild(this.moreItem); + + var undoLastAction = function(){ + this.getActionTracker().undo(this.autoselect ? 2 : 1); + + this.removeEventListener("stoprename", undoLastAction); + this.removeEventListener("beforerename", removeSetRenameEvent); + this.removeEventListener("afterrename", afterRename); + } + var afterRename = function(){ + //this.select(addedNode); + this.removeEventListener("afterrename", afterRename); + }; + var removeSetRenameEvent = function(e){ + this.removeEventListener("stoprename", undoLastAction); + this.removeEventListener("beforerename", removeSetRenameEvent); + + //There is already a choice with the same value + var xmlNode = this.findXmlNodeByValue(e.args[1]); + if (xmlNode || !e.args[1]) { + if (e.args[1] && this.dispatchEvent("notunique", { + value : e.args[1] + }) === false) { + this.startRename(); + + this.addEventListener("stoprename", undoLastAction); + this.addEventListener("beforerename", removeSetRenameEvent); + } + else { + this.removeEventListener("afterrename", afterRename); + + this.getActionTracker().undo();//this.autoselect ? 2 : 1); + if (!this.isSelected(xmlNode)) + this.select(xmlNode); + } + + return false; + } + }; + + this.addEventListener("stoprename", undoLastAction); + this.addEventListener("beforerename", removeSetRenameEvent); + this.addEventListener("afterrename", afterRename); + + + this.startDelayedRename({}, 1); + + }); + }; + + /**** Selection ****/ + + this.$calcSelectRange = function(xmlStartNode, xmlEndNode){ + var r = [], + nodes = this.hasFeature(apf.__VIRTUALVIEWPORT__) + ? this.xmlRoot.selectNodes(this.each) + : this.getTraverseNodes(), + f, i; + for (f = false, i = 0; i < nodes.length; i++) { + if (nodes[i] == xmlStartNode) + f = true; + if (f) + r.push(nodes[i]); + if (nodes[i] == xmlEndNode) + f = false; + } + + if (!r.length || f) { + r = []; + for (f = false, i = nodes.length - 1; i >= 0; i--) { + if (nodes[i] == xmlStartNode) + f = true; + if (f) + r.push(nodes[i]); + if (nodes[i] == xmlEndNode) + f = false; + } + } + + return r; + }; + + this.$selectDefault = function(XMLRoot){ + this.select(this.getTraverseNodes()[0], null, null, null, true); + }; + + /** + * Generates a list of items based on a string. + * @param {String} str the description of the items. Items are seperated by a comma (,). Ranges are specified by a start and end value seperated by a dash (-). + * Example: + * This example loads a list with items starting at 1980 and ending at 2050. + * + * lst.loadFillData("1980-2050"); + * lst.loadFillData("red,green,blue,white"); + * lst.loadFillData("None,100-110,1000-1100"); + * lst.loadFillData("1-10"); // 1 2 3 4 etc + * lst.loadFillData("01-10"); //01, 02, 03, 04, etc + * + */ + this.loadFillData = function(str){ + var len, start, end, parts = str.splitSafe(","), data = []; + + for (var p, part, i = 0; i < parts.length; i++) { + if ((part = parts[i]).match(/^\d+-\d+$/)) { + p = part.split("-"); + start = parseInt(p[0]); + end = parseInt(p[1]); + + if (p[0].length == p[1].length) { + len = Math.max(p[0].length, p[1].length); + for (var j = start; j < end + 1; j++) { + data.push("" + (j + "").pad(len, "0") + ""); + } + } + else { + for (var j = start; j < end + 1; j++) { + data.push("" + j + ""); + } + } + } + else { + data.push("" + part + ""); + } + } + + //@todo this is all an ugly hack (copied from item.js line 486) + //this.$preventDataLoad = true;//@todo apf3.0 add remove for this + + this.$initingModel = true; + + this.each = "item"; + this.$setDynamicProperty("caption", "[label/text()|@caption|text()]"); + this.$setDynamicProperty("eachvalue", "[value/text()|@value|text()]"); + this.$canLoadDataAttr = false; + + this.load("" + data.join("") + ""); + }; + +}).call(apf.BaseList.prototype = new apf.MultiSelect()); + + + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/core/baseclasses/basesimple.js)SIZE(1729)TIME(Fri, 14 Oct 2011 08:01:44 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + +/** + * Baseclass of a simple element. This are usually displaying elements + * (i.e. {@link element.label}, {@link element.picture}) + * + * @constructor + * @baseclass + * + * @inherits apf.StandardBinding + * @inherits apf.DataAction + * + * @author Ruben Daniels (ruben AT ajax DOT org) + * @version %I%, %G% + * @since 0.8 + */ +apf.BaseSimple = function(){ + this.$init(true); +}; + +(function() { + + this.implement(apf.DataAction); + + + this.getValue = function(){ + return this.value; + }; + +}).call(apf.BaseSimple.prototype = new apf.StandardBinding()); + + + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/core/baseclasses/basestatebuttons.js)SIZE(27242)TIME(Fri, 14 Oct 2011 08:01:44 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + +/** + * @constructor + * @baseclass + * + * @author Ruben Daniels (ruben AT ajax DOT org) + * @version %I%, %G% + * @since 0.8 + */ +apf.BaseStateButtons = function(){ + this.state = "normal"; + this.edit = false; + + var actions = { + "min" : ["minimized", "minimize", "restore"], + "max" : ["maximized", "maximize", "restore"], + "edit" : ["edit", "edit", "closeedit"], + "close" : ["closed", "close", "show"] + }; + this.$lastheight = null; + this.$lastpos = null; + + this.$lastState = {"normal":true}; + this.$booleanProperties["animate"] = true; + this.$supportedProperties.push("buttons", "animate", "state"); + + /** + * Close the window. It can be reopened by using {@link baseclass.guielement.method.show} + * Call-chaining is supported. + * @todo show should unset closed + */ + this.close = function(){ + this.setProperty("state", this.state.split("|") + .pushUnique("closed").join("|"), false, true); + return this; + }; + + /** + * Minimize the window. The window will become the height of the title of + * the window. + * Call-chaining is supported. + */ + this.minimize = function(){ + this.setProperty("state", this.state.split("|") + .remove("maximized") + .remove("normal") + .pushUnique("minimized").join("|"), false, true); + return this; + }; + + /** + * Maximize the window. The window will become the width and height of the + * browser window. + * Call-chaining is supported. + */ + this.maximize = function(){ + this.setProperty("state", this.state.split("|") + .remove("minimized") + .remove("normal") + .pushUnique("maximized").join("|"), false, true); + return this; + }; + + /** + * Restore the size of the window. The window will become the width and + * height it had before it was minimized or maximized. + * Call-chaining is supported. + */ + this.restore = function(){ + this.setProperty("state", this.state.split("|") + .remove("minimized") + .remove("maximized") + .pushUnique("normal").join("|"), false, true); + return this; + }; + + /** + * Set the window into edit state. The configuration panel is shown. + * Call-chaining is supported. + */ + this.edit = function(value){ + this.setProperty("state", this.state.split("|") + .pushUnique("edit").join("|"), false, true); + return this; + }; + + /** + * Removes the edit state of this window. The configuration panel is hidden. + * Call-chaining is supported. + */ + this.closeedit = function(value){ + this.setProperty("state", this.state.split("|") + .remove("edit").join("|"), false, true); + return this; + }; + + this.$toggle = function(type){ + var c = actions[type][0]; + this[actions[type][this.state.indexOf(c) > -1 ? 2 : 1]](); + }; + + this.$propHandlers["refparent"] = function(value){ + if (typeof value == "string") + this.$refParent = self[value] && self[value].$ext || document.getElementById(value); + else this.$refParent = value; + } + + this.$propHandlers["maxconf"] = function(value){ + this.$maxconf = value.splitSafe(","); + } + + /** + * @attribute {String} state the state of the window. The state can be a + * combination of multiple states seperated by a pipe '|' character. + * Possible values: + * normal The window has it's normal size and position. Default value. + * minimized The window is minimized. + * maximized The window is maximized. + * edit The window is in the edit state. + * closed The window is closed. + */ + this.$propHandlers["state"] = function(value, prop, force, reenter, noanim){ + var _self = this; + if (!this.$amlLoaded) { //@todo I still think this is weird and should not be needed + apf.queue.add("state" + this.$uniqueId, function(){ + _self.$propHandlers["state"].call(_self, value, prop, force, reenter, noanim); + }); + return; + } + + if (value == 0) + value = "normal"; + + var i, pNode, position, l, t, + o = {}, + s = value.split("|"), + lastState = this.$lastState, + styleClass = []; + + for (i = 0; i < s.length; i++) + o[s[i]] = true; + o.value = value; + + if (!o.maximized && !o.minimized) + o.normal = true; + + if (!reenter && this.dispatchEvent("beforestatechange", { + from : lastState, + to : o}) === false) { + this.state = lastState.value; + return false; + } + + //Closed state + if (o.closed == this.visible) {//change detected + this.setProperty("visible", !o["closed"], false, true); + //@todo difference is, we're not clearing the other states, check the docking example + } + + //Restore state + if (o.normal != lastState.normal + || !o.normal && (o.minimized != lastState.minimized + || o.maximized != lastState.maximized)) { + + if (this.$lastheight) // this.aData && this.aData.hidden == 3 ?? + this.$ext.style.height = this.$lastheight;//(this.$lastheight - apf.getHeightDiff(this.$ext)) + "px"; + + if (this.$lastpos) { + apf.plane.hide(this.$uniqueId); + + if (this.animate && !noanim) { + //Pre remove paused event because of not having onresize + //if (apf.hasSingleRszEvent) + //delete apf.layout.onresize[apf.layout.getHtmlId(this.$pHtmlNode)]; + + var htmlNode = this.$ext; + position = apf.getStyle(htmlNode, "position"); + if (position != "absolute") { + l = parseInt(apf.getStyle(htmlNode, "left")) || 0; + t = parseInt(apf.getStyle(htmlNode, "top")) || 0; + } + else { + l = htmlNode.offsetLeft; + t = htmlNode.offsetTop; + } + + this.animstate = 1; + apf.tween.multi(htmlNode, { + steps : 15, + anim : apf.tween.easeInOutCubic, + interval : 10, + tweens : [ + {type: "left", from: l, to: this.$lastpos.px[0]}, + {type: "top", from: t, to: this.$lastpos.px[1]}, + {type: "width", from: this.$ext.offsetWidth, + to: this.$lastpos.px[2]}, + {type: "height", from: this.$ext.offsetHeight, + to: this.$lastpos.px[3]} + ], + oneach : function(){ + + if (apf.hasSingleRszEvent) + apf.layout.forceResize(_self.$int); + + }, + onfinish : function(){ + _self.$lastpos.parentNode.insertBefore(_self.$ext, _self.$lastpos.beforeNode); + + if (_self.$placeHolder) + _self.$placeHolder.parentNode.removeChild(_self.$placeHolder); + + _self.$propHandlers["state"].call(_self, value, null, + null, true, true); + } + }); + + return; + } + else if (!this.animate) { + apf.plane.hide(this.$uniqueId, true); + + _self.$lastpos.parentNode.insertBefore(_self.$ext, _self.$lastpos.beforeNode); + + if (_self.$placeHolder) + _self.$placeHolder.parentNode.removeChild(_self.$placeHolder); + } + + this.$ext.style.position = this.$lastpos.pos; + this.$ext.style.left = this.$lastpos.css[0]; + this.$ext.style.top = this.$lastpos.css[1]; + this.$ext.style.width = this.$lastpos.css[2]; + this.$ext.style.height = this.$lastpos.css[3]; + + pNode = this.$lastpos.parentNode; + pNode.style.width = this.$lastpos.parent[0]; + pNode.style.height = this.$lastpos.parent[1]; + pNode.style.overflow = this.$lastpos.parent[2]; + } + + + if (this.aData && this.aData.restore) + this.aData.restore(); + + + + if (apf.layout) + apf.layout.play(this.$pHtmlNode); + + + this.$lastheight = this.$lastpos = null; + + if (o.normal) + styleClass.push("", + this.$baseCSSname + "Max", + this.$baseCSSname + "Min"); + } + + if (o.minimized != lastState.minimized) { + if (o.minimized) { + styleClass.unshift( + this.$baseCSSname + "Min", + this.$baseCSSname + "Max", + this.$baseCSSname + "Edit"); + + + if (this.aData && this.aData.minimize) + this.aData.minimize(this.collapsedHeight); + + + if (!this.aData || !this.aData.minimize) { + this.$lastheight = apf.getStyle(this.$ext, "height");//this.$ext.offsetHeight; + + this.$ext.style.height = Math.max(0, this.collapsedHeight + - apf.getHeightDiff(this.$ext)) + "px"; + } + + if (this.hasFocus()) + apf.window.moveNext(null, this, true); + //else if(apf.document.activeElement) + //apf.document.activeElement.$focus({mouse: true}); + } + else { + styleClass.push(this.$baseCSSname + "Min"); + + $setTimeout(function(){ + apf.window.$focusLast(_self); + }); + } + } + + if (o.maximized != lastState.maximized) { + if (o.maximized) { + styleClass.unshift( + this.$baseCSSname + "Max", + this.$baseCSSname + "Min", + this.$baseCSSname + "Edit"); + + pNode = this.$refParent; + if (!pNode) + pNode = (this.$ext.offsetParent == document.body + ? document.documentElement + : this.$ext.parentNode); + + this.animstate = 0; + var hasAnimated = false, htmlNode = this.$ext; + + var position = apf.getStyle(htmlNode, "position"); + if (position == "absolute") { + pNode.style.overflow = "hidden"; + l = htmlNode.offsetLeft; + t = htmlNode.offsetTop; + } + else { + var pos = apf.getAbsolutePosition(htmlNode); //pNode + l = pos[0];//parseInt(apf.getStyle(htmlNode, "left")) || 0; + t = pos[1];//parseInt(apf.getStyle(htmlNode, "top")) || 0; + } + + this.$lastpos = { + css : [this.$ext.style.left, this.$ext.style.top, + this.$ext.style.width, this.$ext.style.height, + this.$ext.style.margin, this.$ext.style.zIndex], + px : [l, t, this.$ext.offsetWidth, + this.$ext.offsetHeight], + parent : [pNode.style.width, pNode.style.height, + pNode.style.overflow], + pos : htmlNode.style.position, + parentNode : pNode, + beforeNode : this.$ext.nextSibling + }; + + if (this.parentNode.$layout) { + if (!this.$placeHolder) + this.$placeHolder = document.createElement("div"); + this.$placeHolder.style.position = this.$lastpos.pos; + this.$placeHolder.style.left = this.$lastpos.css[0]; + this.$placeHolder.style.top = this.$lastpos.css[1]; + this.$placeHolder.style.width = this.$lastpos.px[2] + "px"; + this.$placeHolder.style.height = this.$lastpos.px[3] + "px"; + this.$placeHolder.style.margin = this.$lastpos.css[4]; + this.$placeHolder.style.zIndex = this.$lastpos.css[5]; + this.$pHtmlNode.insertBefore(this.$placeHolder, this.$ext); + + htmlNode.style.position = "absolute"; + } + + document.body.appendChild(htmlNode); + htmlNode.style.left = l + "px"; + htmlNode.style.top = t + "px"; + + function setMax(){ + //While animating dont execute this function + if (_self.animstate) + return; + + var w, h, pos, box, pDiff; + if (_self.maxconf) { + w = _self.$maxconf[0]; + h = _self.$maxconf[1]; + + pos = [_self.$maxconf[2] == "center" + ? (apf.getWindowWidth() - w)/2 + : _self.$maxconf[2], + _self.$maxconf[3] == "center" + ? (apf.getWindowHeight() - h)/3 + : _self.$maxconf[3]]; + } + else { + w = !apf.isIE && pNode == document.documentElement + ? window.innerWidth + : pNode.offsetWidth, + h = !apf.isIE && pNode == document.documentElement + ? window.innerHeight + : pNode.offsetHeight; + } + + if (!pos) { + pos = pNode != htmlNode.offsetParent + ? apf.getAbsolutePosition(pNode, htmlNode.offsetParent) + : [0, 0]; + } + + if (position != "absolute") { + var diff = apf.getDiff(pNode); + w -= diff[0] + (!_self.$refParent && apf.isIE8 ? 4 : 0);//@todo dirty hack! + h -= diff[0] + (!_self.$refParent && apf.isIE8 ? 4 : 0);//@todo dirty hack! + } + //@todo dirty hack! + else if (!_self.$refParent && apf.isIE8) { + w -= 4; + h -= 4; + } + + box = _self.$refParent ? [0,0,0,0] : marginBox; + pDiff = apf.getDiff(pNode); + + pNode.style.width = (pNode.offsetWidth - pDiff[0]) + "px"; + pNode.style.height = (pNode.offsetHeight - pDiff[1]) + "px"; + + if (!hasAnimated && _self.$maxconf && _self.$maxconf[4]) + apf.plane.show(htmlNode, false, null, null, { + color : _self.$maxconf[4], + opacity : _self.$maxconf[5], + animate : _self.animate, + protect : _self.$uniqueId + }); + + if (_self.animate && !hasAnimated) { + _self.animstate = 1; + hasAnimated = true; + apf.tween.multi(htmlNode, { + steps : 15, + anim : apf.tween.easeInOutCubic, + interval : 10, + tweens : [ + {type: "left", from: l, to: pos[0] - box[3]}, + {type: "top", from: t, to: pos[1] - box[0]}, + {type: "width", from: _self.$lastpos.px[2], + to: (w + box[1] + box[3] - apf.getWidthDiff(_self.$ext))}, + {type: "height", from: _self.$lastpos.px[3], + to: (h + box[0] + box[2] - apf.getHeightDiff(_self.$ext))} + ], + oneach : function(){ + + if (apf.hasSingleRszEvent) + apf.layout.forceResize(_self.$int); + + }, + onfinish : function(){ + _self.animstate = 0; + + _self.dispatchEvent("afterstatechange", { + from : lastState, + to : o + }); + + + if (apf.hasSingleRszEvent) + apf.layout.forceResize(_self.$int); + + } + }); + } + else if (!_self.animstate) { + htmlNode.style.left = (pos[0] - box[3]) + "px"; + htmlNode.style.top = (pos[1] - box[0]) + "px"; + + var diff = apf.getDiff(_self.$ext); + htmlNode.style.width = (w + - diff[0] + box[1] + box[3]) + "px"; + htmlNode.style.height = (h + - diff[1] + box[0] + box[2]) + "px"; + } + } + + + if (apf.layout) + apf.layout.pause(this.$pHtmlNode, setMax); + + } + else { + styleClass.push(this.$baseCSSname + "Max"); + } + } + + if (o.edit != lastState.edit) { + if (o.edit) { + styleClass.unshift( + this.$baseCSSname + "Edit", + this.$baseCSSname + "Max", + this.$baseCSSname + "Min"); + + if (this.btnedit) + oButtons.edit.innerHTML = "close"; //hack + + this.dispatchEvent('editstart'); + } + else { + if (this.dispatchEvent('editstop') === false) + return false; + + styleClass.push(this.$baseCSSname + "Edit"); + if (styleClass.length == 1) + styleClass.unshift(""); + + if (this.btnedit) + oButtons.edit.innerHTML = "edit"; //hack + } + } + + if (styleClass.length || o.closed != lastState.closed) { + if (styleClass.length) + this.$setStyleClass(this.$ext, styleClass.shift(), styleClass); + + if (o.edit) { //@todo apf3.0 + this.dispatchEvent("prop.visible", {value:true}); + + if (_self.oSettings) + apf.layout.forceResize(_self.oSettings); + + } + + //@todo research why this is not symmetrical + if (!o.maximized || !this.animate || lastState.maximized && _self.animate) { + _self.dispatchEvent("afterstatechange", { + from : lastState, + to : o}); + } + + this.$lastState = o; + + + if (this.aData && !o.maximized) { //@todo is this the most optimal position? + this.$purgeAlignment(); + } + + + + if (!this.animate && apf.hasSingleRszEvent && apf.layout) + apf.layout.forceResize(_self.$int); + + } + }; + + var marginBox, hordiff, verdiff, oButtons = {} + /** + * @attribute {String} buttons the buttons that the window displays. This + * can be multiple values seperated by a pipe '|' character. + * Possible values: + * min The button that minimizes the window. + * max The button that maximizes the window. + * close The button that closes the window. + * edit The button that puts the window into the edit state. + */ + this.$propHandlers["buttons"] = function(value){ + + if (!this.$hasLayoutNode("button")) + return; + + var buttons = value && (value = value.replace(/(\|)\||\|$/, "$1")).split("|") || [], + nodes = this.$buttons.childNodes, + re = value && new RegExp("(" + value + ")"), + found = {}, + idleNodes = []; + + //Check if we can 'remove' buttons + for (var i = 0; i < nodes.length; i++) { + if (nodes[i].nodeType != 1 || nodes[i].tagName != "DIV") //@todo temp hack + continue; + + if (nodes[i].getAttribute("button") && (!value + || !nodes[i].className || !nodes[i].className.match(re))) { + nodes[i].style.display = "none"; + this.$setStyleClass(nodes[i], "", ["min", "max", "close", "edit"]); + idleNodes.push(nodes[i]); + } + else { + found[RegExp.$1] = nodes[i]; + } + } + + //Create new buttons if needed + for (i = 0; i < buttons.length; i++) { + if (!buttons[i]) + continue; + + if (found[buttons[i]]) { + this.$buttons.insertBefore(found[buttons[i]], this.$buttons.firstChild); + continue; + } + + var btn = idleNodes.pop(); + if (!btn) { + this.$getNewContext("button"); + btn = this.$getLayoutNode("button"); + btn.setAttribute("button", "button"); + setButtonEvents.call(this, btn); + btn = apf.insertHtmlNode(btn, this.$buttons); + } + + this.$setStyleClass(btn, buttons[i], ["min", "max", "close", "edit"]); + btn.onclick = new Function("apf.lookup(" + this.$uniqueId + ").$toggle('" + + buttons[i] + "')"); + btn.style.display = "block"; + oButtons[buttons[i]] = btn; + this.$buttons.insertBefore(btn, this.$buttons.firstChild); + } + + marginBox = apf.getBox(apf.getStyle(this.$ext, "borderWidth")); + }; + + function setButtonEvents(btn){ + //@todo can this cancelBubble just go? + //event.cancelBubble = true; \ + btn.setAttribute("onmousedown", + "var o = apf.all[" + this.$uniqueId + "];\ + o.$setStyleClass(this, 'down', null, true);\ + apf.cancelBubble(event, o); \ + var o = apf.findHost(this).$ext;\ + if (o.onmousedown) o.onmousedown(event);\ + apf.cancelBubble(event, o);\ + apf.window.$mousedown(event);"); + btn.setAttribute("onmouseup", + "var o = apf.all[" + this.$uniqueId + "];\ + o.$setStyleClass(this, '', ['down'], true);"); + btn.setAttribute("onmouseover", + "var o = apf.all[" + this.$uniqueId + "];\ + o.$setStyleClass(this, 'hover', null, true);"); + btn.setAttribute("onmouseout", + "var o = apf.all[" + this.$uniqueId + "];\ + o.$setStyleClass(this, '', ['hover', 'down'], true);"); + btn.setAttribute("ondblclick", "apf.stopPropagation(event);"); + } + + this.$initButtons = function(oExt){ + this.animate = apf.enableAnim; + + this.collapsedHeight = this.$getOption("Main", "collapsed-height"); + + var oButtons = this.$getLayoutNode("main", "buttons", oExt); + if (!oButtons || apf.isIphone || !this.getAttribute("buttons") + || !this.$hasLayoutNode("button")) + return; + + var len = (this.getAttribute("buttons") || "").split("|").length; + for (var btn, i = 0; i < len; i++) { + this.$getNewContext("button"); + btn = oButtons.appendChild(this.$getLayoutNode("button")); + btn.setAttribute("button", "button"); + setButtonEvents.call(this, btn); + } + }; + + this.addEventListener("DOMNodeRemovedFromDocument", function(e){ + for (var name in oButtons) { + oButtons[name].onclick = null; + } + }); +}; + + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/core/baseclasses/basetab.js)SIZE(57877)TIME(Mon, 19 Dec 2011 11:09:29 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + + +/** + * Baseclass of a paged element. + * + * @constructor + * @baseclass + * @allowchild page + * @author Ruben Daniels (ruben AT ajax DOT org) + * @version %I%, %G% + * @since 0.8 + * + * @event beforeswitch Fires before this element switches to another page. + * cancelable: Prevents the page to become active. + * object: + * {Mixed} previous the name or number of the current page. + * {Number} previousId the number of the current page. + * {apf.page} previousPage the current page. + * {Mixed} next the name or number of the page the will become active. + * {Number} nextId the number of the page the will become active. + * {apf.page} nextPage the page the will become active. + * @event afterswitch Fires after this element has switched to another page. + * object: + * {Mixed} previous the name or number of the previous page. + * {Number} previousId the number of the previous page. + * {apf.page} previousPage the previous page. + * {Mixed} next the name or number of the current page. + * {Number} nextId the number of the the current page. + * {apf.page} nextPage the the current page. + */ +apf.BaseTab = function(){ + this.$init(true); +}; + +(function() { + this.isPaged = true; + this.$focussable = apf.KEYBOARD; + this.length = 0; + this.isLoading = {}; + this.inited = + this.ready = false; + this.$scroll = true; + + /** + * Sets the current page of this element. + * @param {mixed} page the name of numer of the page which is made active. + * @param {Function} callback the function called after setting the page. Especially handy when using the src attribute. + */ + this.set = function(page, callback, noEvent){ + if (noEvent || this.src && !this.$findPage(page, {})) { + return this.$propHandlers["activepage"].call( + this, page, null, null, callback, noEvent); + } + + if (this.activepage == (page.name || page)) + return callback && callback(this.getPage(page)); + + this.$lastCallback = callback; + this.setProperty("activepage", page); + }; + + /**** Properties and Attributes ****/ + + this.$supportedProperties.push("activepage", "activepagenr", "length", + "src", "loading", "trans-in", "trans-out"); + + /** + * @attribute {Number} activepagenr the child number of the active page. + * Example: + * This example uses property binding to maintain consistency between a + * dropdown which is used as a menu, and a pages element + * + * + * Home + * General + * Advanced + * + * + * + * + *

Home Page

+ *
+ * + *

General Page

+ *
+ * + *

Advanced Page

+ *
+ *
+ *
+ */ + this.$propHandlers["activepagenr"] = + + /** + * @attribute {String} activepage the name of the active page. + * Example: + * + * + * + * ... + * + * + * ... + * + * + * ... + * + * + * + */ + this.$propHandlers["activepage"] = function(next, prop, force, callback, noEvent){ + if (!this.inited || apf.isNot(next) || next == -1) return; + + if (!callback) { + callback = this.$lastCallback; + delete this.$lastCallback; + } + + var page, info = {}; + page = this.$findPage(next, info); + + if (!page) { + if (this.src) { + if (this.isLoading[next]) + return; + + if (this.$findPage("loading", {})) + this.$propHandlers["activepage"].call(this, "loading"); + + this.setProperty("loading", true); + this.isLoading[next] = true; + + page = this.ownerDocument.createElementNS(apf.ns.apf, "page"); + page.setAttribute("id", next); + this.appendChild(page); + + var _self = this; + page.insertMarkup(this.src, { + page : next, + //@todo apf3.0 change callback arguments in xinclude + callback : function(options){ + delete _self.isLoading[next]; + + if (!options.xmlNode) { + var oError = new Error(apf.formatErrorString(0, null, + "Loading new page", "Could not load new page: " + + _self.src)); + + _self.setProperty("loading", false); + + if (this.dispatchEvent("error", apf.extend({ + error : oError, + bubbles : true + }, options)) === false) + return true; + + throw oError; + } + else { + //for success + _self.setProperty("activepage", next); + + //Needs to be after set + if (callback) + callback(options.amlNode); + + _self.setProperty("loading", false); + } + } + }); + return; + } + + + + return false; + } + + if (page.parentNode != this) { + + + return false; + } + + if (!page.visible || page.disabled) { + + + return false; + } + + //If page is given as first argument, let's use its position + if (next.tagName) { + next = info.position; + this.activepage = page.name || next;//page.type || + } + + //Call the onbeforeswitch event; + if (!noEvent) { + var oEvent = { + previous : this.activepage, + previousId : this.activepagenr, + previousPage : this.$activepage, + next : next, + nextId : info.position, + nextPage : page + }; + + if (this.dispatchEvent("beforeswitch", oEvent) === false) { + //Loader support + if (this.hideLoader) + this.hideLoader(); + + return false; + } + } + + //Maintain an activepagenr property (not reentrant) + this.activepagenr = info.position; + this.setProperty("activepagenr", info.position); + + //Deactivate the current page, if any, and activate the new one + if (this.$activepage) + this.$activepage.$deactivate(); + + page.$activate(); + + + if (page["trans-in"] || this.$activepage && this.$activepage["trans-out"]) + this.transition(page, page["trans-in"] || "normal", + this.$activepage, this.$activepage && this.$activepage["trans-out"] || "normal"); + + + this.$activepage = page; + + //this.scrollIntoView(page); + + + //Loader support + if (this.hideLoader) { + if (page.$rendered !== false) { + this.hideLoader(); + } + else { + //Delayed rendering support + page.addEventListener("afterrender", function(){ + this.parentNode.hideLoader(); + }); + } + } + + if (!noEvent) { + if (page.$rendered !== false) + this.dispatchEvent("afterswitch", oEvent); + else { + //Delayed rendering support + page.addEventListener("afterrender", function(){ + this.parentNode.dispatchEvent("afterswitch", oEvent); + }); + } + } + + if (typeof callback == "function") + callback(page); + + return true; + }; + + /** + * @attribute {String} buttons the modifier for tab page buttons, seperated by a | character + * Possible values: + * close the button has a close button inside it. + * scale the buttons are scaled to make room for more buttons. + * scroll when the buttons take too much space scroll buttons are displayed. + */ + this.$propHandlers["buttons"] = function(value){ + //this.buttons = value; + this.$scale = value.indexOf("scale") > -1; + this.$scroll = !this.$scale; + this.$order = value.indexOf("order") > -1; + + + //@todo skin change + //@todo buttons on the side + if (this.$scale) { + this.$maxBtnWidth = parseInt(this.$getOption("button", "maxwidth")) || 150; + this.$minBtnWidth = parseInt(this.$getOption("button", "minwidth")) || 10; + this.$setStyleClass(this.$buttons, "scale"); + this.addEventListener("resize", scalersz); + + this.minwidth = this.$minBtnWidth * this.getPages().length + 10; + this.$ext.style.minWidth = Math.max(0, this.minwidth - apf.getWidthDiff(this.$ext)) + "px"; + } + else { + this.$setStyleClass(this.$buttons, "", ["scale"]); + this.removeEventListener("resize", scalersz); + } + + }; + + + function visCheck(){ + scalersz.call(this) + } + + this.anims = "add|remove|sync"; + + this.$scaleinit = function(node, type, callback, force){ + var pg = this.getPages(); + var l = pg.length; + this.minwidth = this.$minBtnWidth * l + 10; //@todo padding + margin of button container + this.$ext.style.minWidth = Math.max(0, this.minwidth - apf.getWidthDiff(this.$ext)) + "px"; + + if (force && !this.$ext.offsetWidth && !this.$ext.offsetHeight + || this.anims.indexOf(type) == -1) { + scalersz.call(this); + + if (type == "add") + node.dispatchEvent("afteropen"); + else if (type == "remove") + node.dispatchEvent("afterclose"); + + return false; + } + + if (!apf.window.vManager.check(this, "tabscale", visCheck)) + return; + + if (!type) + return scalersz.call(this); + + if (this.$control && this.$control.type != "remove" && this.$control.stop) + this.$control.stop(); + + var _self = this; + var anim = { + steps : type == "remove" ? 8 : 8, + control : this.$control = {}, + anim : apf.tween.EASEOUT, + interval : 10, + tweens : [], + oHtml : node, + onfinish : function(){ + if (!node) + return; + + if (type == "add") + node.dispatchEvent("afteropen"); + }, + onstop : function(){ + if (!node) + return; + + if (type == "add") + node.dispatchEvent("afteropen"); + else if (type == "remove") + node.dispatchEvent("afterclose"); + } + //oneach : function(){alert(1);} + }; + + function btnMoHandler(e){ + var pos = apf.getAbsolutePosition(this); + if (e.clientX <= pos[0] || e.clientY <= pos[1] + || e.clientX >= pos[0] + this.offsetWidth + || e.clientY >= pos[1] + this.offsetHeight) { + apf.removeListener(_self.$buttons, "mouseout", btnMoHandler); + if (_self.$control.state == apf.tween.STOPPED) { + delete _self.$waitForMouseOut; + _self.$scaleinit(null, "sync"); + } + else if (_self.$waitForMouseOut) + _self.$waitForMouseOut = 2; + } + } + + this.$control.type = type; + + if (type == "add") { + var htmlNode = node.$button; + htmlNode.style.width = this.$minBtnWidth + "px"; + if (pg.length) { + scalersz.call(this, null, node); + this.$buildScaleAnim(anim, pg, null, true); + } + } + else if (type == "sync") { + this.$buildScaleAnim(anim, pg); + } + else if (type == "remove") { + anim.onfinish = function(){ + if (node.dispatchEvent("afterclose") !== false) + callback(); + + html.style.marginLeft = 0; + apf.setOpacity(html, 1); + + if (_self.$waitForMouseOut == 2) { + apf.removeListener(_self.$buttons, "mouseout", btnMoHandler); + delete _self.$waitForMouseOut; + _self.$scaleinit(null, "sync"); + } + else if (isLast) + delete _self.$waitForMouseOut; + } + anim.onstop = function(){ + apf.setOpacity(html, 1); + } + + var html = node.$button; + anim.tweens.push({ + oHtml : html, + type : "width", + from : html.offsetWidth - apf.getWidthDiff(html), + to : 0 + }); + var over = apf.getWidthDiff(html) + (this.$btnMargin || 0); + if (over) + anim.tweens.push({ + oHtml : html, + type : "marginLeft", + from : 0, + to : -1 * over + }); + anim.tweens.push({ + oHtml : html, + type : "fade", + from : 1, + to : 0 + }); + + var isLast = pg[pg.length - 1] == node; + if (isLast) + this.$buildScaleAnim(anim, pg, node); + + //Set activetab if the current one is lost + if (this.nextTabInLine) { + this.set(this.nextTabInLine); + delete this.nextTabInLine; + } + else if (this.$activepage == node) { + var ln = node.nextSibling; + while (ln && (!ln.$first || !ln.visible)) + ln = ln.nextSibling; + var rn = node.previousSibling; + while (rn && (!rn.$last || !rn.visible)) + rn = rn.previousSibling; + if (ln || rn) + this.set(ln || rn); + } + + this.$waitForMouseOut = true; + if (!isLast) + apf.addListener(_self.$buttons, "mouseout", btnMoHandler); + } + + if (anim.tweens.length) + apf.tween.multi(this, anim); + } + + this.$buildScaleAnim = function(anim, pg, excl, add){ + if (excl) { + pg = pg.slice(); + pg.remove(excl); + } + if (!pg.length) + return; + + var cw = this.$buttons.offsetWidth - apf.getWidthDiff(this.$buttons);//apf.getHtmlInnerWidth(this.$ext); + var l = pg.length; + var bw = Math.min(cw/l, this.$maxBtnWidth); + var re = Math.round((bw % 1) * 10); + for (var wd, html, s, i = 0; i < l - 1; i++) { + s = Math.max(this.$minBtnWidth, round[i < re ? 1 : 0](bw)); + cw -= s; + html = pg[i].$button, wd = apf.getWidthDiff(html); + anim.tweens.push({ + oHtml : html, + type : "width", + from : html.offsetWidth - wd, + to : s - wd - (this.$btnMargin || 0) + }); + } + html = pg[l - 1].$button, wd = apf.getWidthDiff(html); + anim.tweens.push({ + oHtml : html, + type : "width", + from : html.offsetWidth - wd, // - (add ? 3 : 0) + to : Math.max(this.$minBtnWidth, + Math.min(cw, this.$maxBtnWidth)) - (this.$btnMargin || 0) - wd + }); + } + + var round = [Math.floor, Math.ceil]; + function scalersz(e, excl){ + if (!this.length && !this.getPages().length || this.$waitForMouseOut + || this.$control && this.$control.state == apf.tween.RUNNING) { + //@todo queue call here to after anim + return; + } + + var page = this.getPage(); + + if (!page) + return; + + if (this.$btnMargin == undefined) + this.$btnMargin = apf.getMargin(page.$button)[0]; + + var pg = this.getPages(); + if (excl) + pg.remove(excl); + if (!pg.length) + return; + + var cw = this.$buttons.offsetWidth - apf.getWidthDiff(this.$buttons) + - (excl ? excl.$button.offsetWidth + this.$btnMargin: 0);//apf.getHtmlInnerWidth(this.$ext); + var l = pg.length; + var bw = Math.min(cw/l, this.$maxBtnWidth); + var re = Math.round((bw % 1) * 10); + for (var s, i = 0; i < l - 1; i++) { + s = Math.max(this.$minBtnWidth, round[i < re ? 1 : 0](bw)); + cw -= s; + if (!pg[i].$button) continue; + pg[i].$button.style.width = (s - apf.getWidthDiff(pg[i].$button) - this.$btnMargin) + "px"; + } + if (!pg[l - 1].$button) return; + pg[l - 1].$button.style.width = (Math.max(this.$minBtnWidth, + Math.min(cw, this.$maxBtnWidth)) + - this.$btnMargin + - apf.getWidthDiff(pg[l - 1].$button)) + "px"; + } + + + /**** Public methods ****/ + + + this.transition = function(pageIn, animIn, pageOut, animOut){ + var _self = this; + + if (!this.$transInfo) { + this.$int.style.overflow = apf.getStyle(this.$int, 'overflow') || "hidden"; + + this.$transInfo = { + start : function(){ + var h = _self.$ext; + this.size = [h.style.width, h.style.height]; + var d = apf.getDiff(h); + h.style.width = (h.offsetWidth - d[0]) + "px"; + h.style.height = (h.offsetHeight - d[1]) + "px"; + + //@todo start anims + if (this["in"]) { + var h = this["in"].oHtml.$ext; + var d = apf.getDiff(h); + h.style.width = (_self.$int.offsetWidth - d[0]) + "px"; + h.style.height = (_self.$int.offsetHeight - d[1]) + "px"; + h.style.display = "block"; + apf.tween.multi(h, this["in"]); + } + if (this["out"]) { + var h = this["out"].oHtml.$ext; + var d = apf.getDiff(h); + h.style.width = (_self.$int.offsetWidth - d[0]) + "px"; + h.style.height = (_self.$int.offsetHeight - d[1]) + "px"; + h.style.display = "block"; + apf.tween.multi(h, this["out"]); + } + }, + + stop : function(){ + if (this["in"] && this["in"].control.stop) + this["in"].control.stop(); + if (this["out"] && this["out"].control.stop) + this["out"].control.stop(); + }, + + finish : function(){ + //@todo buffer calls with timeout + var h = _self.$ext; + h.style.width = this.size[0]; //@todo possibly anim to new size + h.style.height = this.size[1]; + + if (this["in"]) { + var h = this["in"].oHtml.$ext; + h.style.width = this["in"].size[0]; + h.style.height = this["in"].size[1]; + h.style.display = ""; + h.style.position = ""; + h.style.zIndex = ""; + h.style.left = ""; + h.style.top = ""; + apf.setOpacity(h, 1); + delete this["in"]; + } + if (this["out"]) { + var h = this["out"].oHtml.$ext; + h.style.width = this["out"].size[0]; + h.style.height = this["out"].size[1]; + h.style.display = ""; + h.style.position = ""; + h.style.zIndex = ""; + h.style.left = ""; + h.style.top = ""; + apf.setOpacity(h, 1); + delete this["out"]; + } + + _self.oPages.style.width = ""; + _self.oPages.style.height = ""; + } + }; + } + + //stop + this.$transInfo.stop(); + + var d = apf.getDiff(this.oPages); + this.oPages.style.width = (this.oPages.offsetWidth - d[0]) + "px"; + this.oPages.style.height = (this.oPages.offsetHeight - d[1]) + "px"; + + var preventNext = this.$createAnim(pageIn, animIn, false, pageOut); + if (preventNext !== false && pageOut) + this.$createAnim(pageOut, animOut, true, pageIn); + + $setTimeout(function(){ + _self.$transInfo.start(); + }); + } + + this.$cube = { + "left" : [-1, "offsetWidth", "left", "getHtmlInnerWidth"], + "right" : [1, "offsetWidth", "left", "getHtmlInnerWidth"], + "top" : [-1, "offsetHeight", "top", "getHtmlInnerHeight"], + "bottom" : [1, "offsetHeight", "top", "getHtmlInnerHeight"] + } + + this.$createAnim = function(page, animType, out, pageOut){ + var _self = this; + + //create new anim + var anim = { + steps : apf.isIE ? 15 : 25, + control : {}, + anim : out ? apf.tween.EASEOUT : apf.tween.EASEOUT, + interval : 10, + tweens : [], + oHtml : page, + size : [page.$ext.style.width, page.$ext.style.height], + onfinish : function(){ + _self.$transInfo.finish(out); + } + }; + this.$transInfo[out ? "out" : "in"] = anim; + + var from, to, h = page.$ext; + h.style.zIndex = out ? 10 : 20; + h.style.position = "absolute"; + h.style.left = 0; + h.style.top = 0; + h.style.display = "block"; + + animType = animType.split("-"); + switch (animType[0]) { + case "fade": + anim.anim = apf.tween.NORMAL; + if (out) h.style.zIndex = 30; + anim.tweens.push( + out + ? {type: "fade", from: 1, to: 0} + : {type: "fade", from: 0, to: 1} + ); + break; + case "slide": + var info = this.$cube[animType[1]] + from = 0; + to = info[0] * h[info[1]]; + if (!out) + h.style[info[2]] = from + "px"; + + anim.tweens.push({type: info[2], from: out ? from : to, to: out ? to : from}); + //else etc + break; + case "push": + var info = this.$cube[animType[1]] + var h2 = pageOut.$ext; + + if (out) { + if (this.$transInfo["in"]) + this.$transInfo["in"].tweens = []; //prevent in animation + } + else + this.$createAnim(pageOut, "normal", true); + + var hInt = apf[info[3]](this.$int); + + var from1 = info[0] * hInt;//h[info[1]]; + var to1 = 0; + + var from2 = 0; + var to2 = -1 * info[0] * hInt;//h2[info[1]]; + + if (out) + h2.style[info[2]] = to2 + "px"; + else + h.style[info[2]] = from1 + "px"; + + anim.tweens.push({oHtml: h, type: [info[2]], from: out ? to1 : from1, to: out ? from1 : to1}); + anim.tweens.push({oHtml: h2, type: [info[2]], from: out ? to2 : from2, to: out ? from2 : to2}); + + return false; + case "normal": + break; + default: + throw new Error("Unknown animation type:" + animType[0]); //@todo make into proper apf3.0 error + } + } + + + /** + * Retrieves an array of all the page elements of this element. + */ + this.getPages = function(){ + var r = [], nodes = this.childNodes; + for (var i = 0, l = nodes.length; i < l; i++) { + if ("page|case".indexOf(nodes[i].localName) > -1 && nodes[i].visible !== false) + r.push(nodes[i]); + } + return r; + }; + + /** + * Retrieves a page element by it's name or child number + * @param {mixed} nameOrId the name or child number of the page element to retrieve. + * @return {Page} the found page element. + */ + this.getPage = function(nameOrId){ + if (apf.isNot(nameOrId)) + return this.$activepage; + else + return this.$findPage(nameOrId); + }; + + /** + * Add a new page element + * @param {String} [caption] the text displayed on the button of the page. + * @param {String} [name] the name of the page which is can be referenced by. + * @return {page} the created page element. + */ + this.add = function(caption, name, type, before, callback){ + var page = this.ownerDocument.createElementNS(apf.ns.aml, "page"); + if (name) + page.setAttribute("id", name); + if (type) + page.setAttribute("type", type); + if (caption) + page.setAttribute("caption", caption); + + if (callback) + callback(page); + + this.insertBefore(page, before); + + + //this.scrollIntoView(page); + + return page; + }; + + /** + * Removes a page element from this element. This function destroys ALL children + * of this page. To simple remove the page from the DOM tree use the + * removeNode() method. + * + * @param {mixed} nameOrId the name or child number of the page element to remove. + * @return {Page} the removed page element. + */ + this.remove = function(nameOrId, force, noAnimation){ + var page = typeof nameOrId == "object" + ? nameOrId + : this.$findPage(nameOrId); + if (!page) + return false; + + var e = {page: page}; + if (typeof force == "object") { + e.htmlEvent = force; + force = false; + } + + if (!force && this.dispatchEvent("close", e) === false) + return; + + + if (this.$scale && !noAnimation) { + this.$scaleinit(page, "remove", function(){ + //page.removeNode(); + page.destroy(true, true); + }, true); + } + else + + { + //page.removeNode(); + if (page.dispatchEvent("afterclose") !== false) + page.destroy(true, true); + + + //@todo this is wrong, we can also use removeChild + //this.setScrollerState(); + + } + + return page; + }; + + + /* + var SCROLLANIM = { + scrollOn : false, + steps : 15, + interval : 10, + size : 0, + left : 0, + control : { + stop : false + }, + stopHandle: function() { + bAnimating = false; + } + }, + SCROLL_OFF = 0x0001, + SCROLL_HOVER = 0x0002, + SCROLL_DOWN = 0x0004, + SCROLL_DIS = 0x0008, + SCROLL_L_STATE = SCROLL_OFF, + SCROLL_R_STATE = SCROLL_OFF, + SCROLL_LEFT = 0x0001, + SCROLL_RIGHT = 0x0002, + SCROLL_BOTH = 0x0004, + bAnimating = false, + scrollTimer = null, + keepScrolling = false, + globalDir = SCROLL_LEFT; +*/ + function getButtonsWidth() { + var cId = "cache_" + this.$buttons.childNodes.length; + if (SCROLLANIM[cId]) + return SCROLLANIM[cId]; + + var iWidth = 0; + for (var i = 0, l = this.$buttons.childNodes.length; i < l; i++) { + if (typeof this.$buttons.childNodes[i].offsetWidth != "undefined") + iWidth += this.$buttons.childNodes[i].offsetWidth; + } + + return SCROLLANIM[cId] = iWidth; + } + + function setButtonState(dir, state) { + var bBoth = dir & SCROLL_BOTH; + if (bBoth) + dir = SCROLL_LEFT; + var oBtn = this[dir & SCROLL_LEFT ? "oLeftScroll" : "oRightScroll"]; + if (!(state & SCROLL_DIS)) { + if (dir & SCROLL_LEFT) + SCROLL_L_STATE = state; + else + SCROLL_R_STATE = state; + } + + if (state & SCROLL_OFF) + apf.setStyleClass(oBtn, "", ["disabled", "hover", "down"]); + else if (state & SCROLL_HOVER) + apf.setStyleClass(oBtn, "hover", ["disabled", "down"]); + else if (state & SCROLL_DOWN) + apf.setStyleClass(oBtn, "down", ["disabled", "hover"]); + else if (state & SCROLL_DIS) + apf.setStyleClass(oBtn, "disabled", ["hover", "down"]); + + if (bBoth) + setButtonState(SCROLL_RIGHT, state); + } + + /** + * Set the state scroller buttons: enabled, disabled or completely hidden, + * depending on the state of the tab buttons + * + * @param {Boolean} [bOn] Indicates whether to turn the scroll buttons on or off + * @param {Number} [iBtns] Specifies the buttons to set the state of. Can be SCROLL_LEFT, SCROLL_RIGHT or SCROLL_BOTH + * @type {void} + */ + this.setScrollerState = function(bOn, iBtns) { + if (!this.ready || !this.$hasButtons || !this.oScroller) return; + + if (typeof bOn == "undefined") { + var scrollerWidth = this.oScroller.offsetWidth + || parseInt(apf.getStyle(this.oScroller, "width").replace(/(px|em|%)/, "")); + bOn = ((getButtonsWidth.call(this) + scrollerWidth) > this.$ext.offsetWidth); + iBtns = SCROLL_BOTH; + } + + if (iBtns & SCROLL_BOTH && bOn !== SCROLLANIM.scrollOn) { + // in case of HIDING the scroller: check if the anim stuff has reverted + SCROLLANIM.scrollOn = bOn; + if (!bOn) { + this.$buttons.style.left = SCROLLANIM.left + "px"; + this.oScroller.style.display = "none"; + } + //else + // TODO: scroll active tab into view if it becomes hidden beneath scroller node(s) + } + else { + this.oScroller.style.display = ""; + } + + this.oScroller.style.display = (iBtns & SCROLL_BOTH && !bOn) + ? "none" + : ""; + if (typeof iBtns == "undefined") + iBtns = SCROLL_BOTH; + if (!bOn) { + if ((iBtns & SCROLL_LEFT) || (iBtns & SCROLL_BOTH)) + setButtonState.call(this, SCROLL_LEFT, SCROLL_DIS); + if ((iBtns & SCROLL_RIGHT) || (iBtns & SCROLL_BOTH)) + setButtonState.call(this, SCROLL_RIGHT, SCROLL_DIS); + } + }; + + /** + * Corrects the state of the scroller buttons when the state of external + * components change, like on a resize event of a window. + * + * @type {void} + */ + this.correctScrollState = function() { + if (!this.ready || !this.$hasButtons || !this.oScroller) return; + this.setScrollerState(); + }; + + /** + * Retrieves the utmost left or right boundaries of the tab buttons strip that + * can be scrolled to. The tabs cannot scroll any further than these boundaries + * + * @param {Number} dir Determines which boundary side to look at; SCROLL_LEFT or SCROLL_RIGHT + * @param {Boolan} [useCache] Used only when tabs are draggable. Not implemented. + * @type {Number} + */ + function getAnimationBoundary(dir, useCache) { + if (SCROLLANIM.size <= 0) { + SCROLLANIM.left = this.$buttons.offsetLeft; + SCROLLANIM.size = Math.round(this.firstChild.$button.offsetWidth); + } + if (dir & SCROLL_LEFT) { + return SCROLLANIM.left; + } + else if (dir & SCROLL_RIGHT) { + // TODO: support Drag n Drop of tabs... + //if (typeof useCache == "undefined") useCache = false; + //if (!tabcontrol.drag) tabcontrol.drag = {}; + //if (useCache && tabcontrol.drag.boundCache) + // return tabcontrol.drag.boundCache; + var oNode = this.$buttons.childNodes[this.$buttons.childNodes.length - 1]; + + return this.$ext.offsetWidth - (oNode.offsetLeft + oNode.offsetWidth + + (this.oScroller.offsetWidth + 4));// used to be tabcontrol.drag.boundCache; + } + } + + /** + * Event handler; executed when the user pressed one of the two scroll buttons + * (left or right one). If the tab-buttons strip may/ can be scrolled, the + * respective behavior is called. + * + * @param {Event} e Event object, usually a mousedown event from a scroller-button + * @param {Number} dir Direction to scroll; SCROLL_LEFT or SCROLL_RIGHT + * @type {void} + */ + this.scroll = function(e, dir) { + if (!this.ready || !this.$hasButtons || !this.oScroller) return; + if (!e) + e = window.event; + if (typeof e["type"] == "unknown") //scope expired (prolly GC'ed) + e = {type: "click"}; + if (bAnimating && e.type != "dblclick") return; + var bAnimating = true; + + if (typeof dir == "undefined") + dir = SCROLL_LEFT; + + //apf.tween.clearQueue(this.$buttons, true); + var iCurrentLeft = this.$buttons.offsetLeft, + size = e["delta"] ? Math.round(e.delta * 36) : SCROLLANIM.size, + //get maximum left offset for either direction + iBoundary = getAnimationBoundary.call(this, dir), + _self = this; + if (dir & SCROLL_LEFT) { + setButtonState(SCROLL_LEFT, SCROLL_DOWN); + setButtonState(SCROLL_RIGHT, SCROLL_OFF); + if (iCurrentLeft === iBoundary) { + this.setScrollerState(false, SCROLL_LEFT); + return apf.tween.single(this.$buttons, { + steps : SCROLLANIM.steps, + interval: 20, + from : iCurrentLeft, + to : iCurrentLeft + 12, + type : "left", + anim : apf.tween.EASEOUT, + onstop : SCROLLANIM.stopHandle, + onfinish: function(oNode) { + apf.tween.single(oNode, { + steps : SCROLLANIM.steps, + interval: SCROLLANIM.interval, + from : iCurrentLeft + 12, + to : iCurrentLeft, + type : "left", + anim : apf.tween.EASEIN, + onstop : SCROLLANIM.stopHandle, + onfinish: function() { + bAnimating = false; + if (e.name == "mousescroll") + setButtonState(SCROLL_LEFT, SCROLL_OFF); + } + }); + } + }); + } + //one scroll animation scrolls by a SCROLLANIM.size px. + var iTargetLeft = iCurrentLeft + (e.type == "dblclick" ? size * 3 : size); + if (iTargetLeft > iBoundary) + iTargetLeft = iBoundary; + + if (iTargetLeft === iBoundary) + this.setScrollerState(false, SCROLL_LEFT); + this.setScrollerState(true, SCROLL_RIGHT); + + //start animated scroll to the left + apf.tween.single(this.$buttons, { + steps : SCROLLANIM.steps, + interval: SCROLLANIM.interval, + control : SCROLLANIM.control, + from : iCurrentLeft, + to : iTargetLeft, + type : "left", + anim : apf.tween.NORMAL, + onstop : SCROLLANIM.stopHandle, + onfinish: function() { + bAnimating = false; + if (e.name == "mousescroll") + setButtonState(SCROLL_LEFT, SCROLL_OFF); + if (keepScrolling) + _self.scroll(e, globalDir); + } + }); + } + else if (dir & SCROLL_RIGHT) { + this.setScrollerState(true); + setButtonState(SCROLL_RIGHT, SCROLL_DOWN); + setButtonState(SCROLL_LEFT, SCROLL_OFF); + if (iCurrentLeft === iBoundary) { + this.setScrollerState(false, SCROLL_RIGHT); + return apf.tween.single(this.$buttons, { + steps : SCROLLANIM.steps, + interval: 20, + from : iCurrentLeft, + to : iCurrentLeft - 24, + type : "left", + anim : apf.tween.EASEOUT, + onstop : SCROLLANIM.stopHandle, + onfinish: function(oNode, options) { + apf.tween.single(oNode, { + steps : SCROLLANIM.steps, + interval: SCROLLANIM.interval, + from : iCurrentLeft - 24, + to : iCurrentLeft, + type : "left", + anim : apf.tween.EASEIN, + onstop : SCROLLANIM.stopHandle, + onfinish: function() { + bAnimating = false; + if (e.name == "mousescroll") + setButtonState(SCROLL_RIGHT, SCROLL_OFF); + } + }); + } + }); + } + //one scroll animation scrolls by a SCROLLANIM.size px. + var iTargetLeft = iCurrentLeft - (e.type == "dblclick" ? size * 3 : size); + //make sure we don't scroll more to the right than the + //maximum left: + if (iTargetLeft < iBoundary) + iTargetLeft = iBoundary; + //start animated scroll to the right + apf.tween.single(this.$buttons, { + steps : SCROLLANIM.steps, + interval: SCROLLANIM.interval, + control : SCROLLANIM.control, + from : iCurrentLeft, + to : iTargetLeft, + type : "left", + anim : apf.tween.NORMAL, + onstop : SCROLLANIM.stopHandle, + onfinish: function() { + bAnimating = false; + if (e.name == "mousescroll") + setButtonState(SCROLL_RIGHT, SCROLL_OFF); + if (keepScrolling) + _self.scroll(e, globalDir); + } + }); + } + }; + + /** + * If a tabpage is outside of the users' view, this function scrolls that + * tabpage into view smoothly. + * + * @param {page} oPage The page to scroll into view + * @type {void} + */ + this.scrollIntoView = function(oPage) { + bAnimating = false; + if (!this.ready || !this.$hasButtons || !this.oScroller || !oPage.$drawn) + return; + bAnimating = true; + if (this.$buttons.offsetWidth < this.$ext.offsetWidth) + return this.setScrollerState(false); + + var iTabLeft = oPage.$button.offsetLeft, + iTabWidth = oPage.$button.offsetWidth, + iCurrentLeft = this.$buttons.offsetLeft; + + if (SCROLLANIM.size <= 0) { + SCROLLANIM.left = this.$buttons.offsetLeft; + var p = this.firstChild; + while (!p.$button) + p = p.nextSibling; + SCROLLANIM.size = Math.round(p.$button.offsetWidth); + } + this.$buttons.style.left = iCurrentLeft; + + var iRealWidth = this.$ext.offsetWidth, + iScrollCorr = this.oScroller.offsetWidth + 4, + iTargetLeft = null, + dir; + + if ((iTabLeft + iTabWidth) > ((iRealWidth - iScrollCorr) - iCurrentLeft)) { //scroll to the right + iTargetLeft = (-(iTabLeft - SCROLLANIM.left) + + (iRealWidth - iTabWidth - iScrollCorr)); + dir = SCROLL_RIGHT; + } + else if ((iCurrentLeft + iTabLeft) < SCROLLANIM.left) { //sroll to the left + iTargetLeft = SCROLLANIM.left - iTabLeft; + dir = SCROLL_LEFT; + } + + if (iTargetLeft !== null) { + this.setScrollerState(true); + setButtonState(SCROLL_RIGHT, dir & SCROLL_RIGHT ? SCROLL_DOWN : SCROLL_OFF); + setButtonState(SCROLL_LEFT, dir & SCROLL_LEFT ? SCROLL_DOWN : SCROLL_OFF); + apf.tween.clearQueue(this.$buttons, true); + + apf.tween.single(this.$buttons, { + steps : SCROLLANIM.steps, + interval: SCROLLANIM.interval, + from : iCurrentLeft, + to : iTargetLeft, + type : "left", + anim : apf.tween.NORMAL, + onstop : SCROLLANIM.stopHandle, + onfinish: function() { + bAnimating = false; + setButtonState(SCROLL_RIGHT, SCROLL_OFF); + setButtonState(SCROLL_LEFT, SCROLL_OFF); + } + }); + } + else + bAnimating = false; + }; + + + + /**** DOM Hooks ****/ + + this.addEventListener("DOMNodeRemoved", function(e){ + var amlNode = e.currentTarget; + if (e.$doOnlyAdmin || e.relatedNode != this + || amlNode.localName != "page") + return; + + if ((this.activepage || this.activepage == 0) && this.activepage != -1) { + if (this.nextTabInLine) + this.set(this.nextTabInLine); + + if (!this.nextTabInLine && this.$activepage == amlNode) { + var ln = amlNode.nextSibling; + while (ln && (!ln.$first || !ln.visible)) + ln = ln.nextSibling; + var rn = amlNode.previousSibling; + while (rn && (!rn.$last || !rn.visible)) + rn = rn.previousSibling; + + if (this.firstChild == amlNode && ln) + ln && ln.$first(); + if (this.lastChild == amlNode && rn) + rn && rn.$last(); + + if (ln || rn) + this.set(ln || rn); + else { + amlNode.$deactivate(); + + + //this.setScrollerState(); + + this.$activepage = + this.activepage = + this.activepagenr = null; + this.setProperty("activepage", null); + } + } + else { + + //if (this.$scroll) + //this.setScrollerState(); + + + if (this.$scale) + this.$scaleinit(); + + } + + delete this.nextTabInLine; + } + + + this.setProperty("length", this.getPages().length - 1); + + }); + + this.addEventListener("DOMNodeInserted",function(e){ + var amlNode = e.currentTarget; + + if (amlNode.localName != "page" || e.relatedNode != this || amlNode.nodeType != 1) + return; + + var pages = this.getPages(); + + if (!e.$beforeNode) { + var lastChild, pg = pages; + if (lastChild = pg[pg.length - 2]) + lastChild.$last(true); + amlNode.$last(); + } + + var p2, p = pages[0]; //@todo $beforeNode doesnt have to be a page + if (amlNode == p) { + if (p2 = this.getPage(1)) + p2.$first(true); + amlNode.$first(); + } + + if (this.$activepage) { + var info = {}; + this.$findPage(this.$activepage, info); + + if (this.activepagenr != info.position) { + if (parseInt(this.activepage) == this.activepage) { + this.activepage = info.position; + this.setProperty("activepage", info.position); + } + this.activepagenr = info.position; + this.setProperty("activepagenr", info.position); + } + } + else if (!this.activepage && !this.$activepage + && !amlNode.render || amlNode.$rendered) { + this.set(amlNode); + } + + + if (this.$scale && amlNode.visible && !e.$isMoveWithinParent) + this.$scaleinit(amlNode, "add"); + else + + { + amlNode.dispatchEvent("afteropen"); + } + + + this.setProperty("length", this.getPages().length); + + }); + + /**** Private state handling functions ****/ + + this.$findPage = function(nameOrId, info){ + var node, nodes = this.childNodes; + + if (nameOrId.localName) { + for (var t = 0, i = 0, l = nodes.length; i < l; i++) { + node = nodes[i]; + if ("page|case".indexOf(node.localName) > -1 && (++t) && node == nameOrId) { + if (info) + info.position = t - 1; + return node; + } + } + } + else { + for (var t = 0, i = 0, l = nodes.length; i < l; i++) { + node = nodes[i]; + if ("page|case".indexOf(node.localName) > -1 && (t++ == nameOrId + || node.name == nameOrId)) { + if (info) + info.position = t - 1; + return node; + } + } + } + + return null; + }; + + this.$enable = function(){ + var nodes = this.childNodes; + for (var i = 0, l = nodes.length; i < l; i++) { + if (nodes[i].enable) + nodes[i].enable(); + } + }; + + this.$disable = function(){ + var nodes = this.childNodes; + for (var i = 0, l = nodes.length; i < l; i++) { + if (nodes[i].disable) + nodes[i].disable(); + } + }; + + /**** Keyboard support ****/ + + + + this.addEventListener("keydown", function(e){ + if (!this.$hasButtons) + return; + + var page, + key = e.keyCode; + + switch (key) { + case 9: + break; + case 13: + break; + case 32: + break; + case 37: //LEFT + page = this.getPage().previousSibling; + while(page && (page.nodeType != 1 + || "page|case".indexOf(page.localName) == -1 || !page.visible)) { + page = page.previousSibling; + } + + if (page) + this.setProperty("activepage", page); + break; + case 39: //RIGHT + page = this.getPage().nextSibling; + while(page && (page.nodeType != 1 + || "page|case".indexOf(page.localName) == -1 || !page.visible)) { + page = page.nextSibling; + } + + if (page) + this.setProperty("activepage", page); + break; + default: + return; + } + //return false; + }, true); + + + + /**** Init ****/ + + this.$loadChildren = function(callback){ + var page = false, + _self = this, + i, j, l, node, nodes; + + this.inited = true; + + if (this.$hasButtons) { + this.$buttons = this.$getLayoutNode("main", "buttons", this.$ext); + this.$buttons.setAttribute("id", this.$uniqueId + "_buttons"); + } + + this.oPages = this.$getLayoutNode("main", "pages", this.$ext); + + + // add scroller node(s) + /*this.oScroller = this.$getLayoutNode("main", "scroller", this.oPages); + if (this.oScroller) { + function startTimer(e, dir) { + clearTimeout(scrollTimer); + globalDir = dir; + scrollTimer = $setTimeout(function() { + keepScrolling = true; + _self.scroll(e, dir); + }, 500); + } + function stopTimer() { + clearTimeout(scrollTimer); + keepScrolling = false; + } + + this.oScroller.onmouseout = function(e) { + SCROLLANIM.control.stop = true; + setButtonState(SCROLL_BOTH, SCROLL_OFF); + }; + + + /*apf.addEventListener("mousescroll", function(e) { + var found = (e.target == _self.$buttons); + while (!found && e.target != document.body) { + e.target = e.target.offsetParent; + found = (e.target == _self.$buttons); + } + if (!found) return; + var dir = e.delta > 0 ? SCROLL_LEFT : SCROLL_RIGHT; + e.delta = Math.abs(e.delta); + _self.scroll(e, dir); + });* / + + + this.oLeftScroll = apf.getNode(this.oScroller, [0]); + this.oRightScroll = apf.getNode(this.oScroller, [1]); + + ["oLeftScroll", "oRightScroll"].forEach(function(sBtn) { + var dir = sBtn == "oLeftScroll" ? SCROLL_LEFT : SCROLL_RIGHT, + revDir = sBtn == "oLeftScroll" ? SCROLL_RIGHT : SCROLL_LEFT; + + _self[sBtn].ondbclick = + _self[sBtn].onmousedown = function(e) { + SCROLLANIM.control.stop = false; + var state = dir & SCROLL_LEFT ? SCROLL_L_STATE : SCROLL_R_STATE; + if (this.className.indexOf("disabled") != -1 + || state & SCROLL_DOWN) return; + e = e || event; + _self.scroll(e, dir); + startTimer(e, dir); + if (!apf.isSafariOld) + this.onmouseout(); + }; + _self[sBtn].onmouseover = function() { + SCROLLANIM.control.stop = false; + var state = dir & SCROLL_LEFT ? SCROLL_L_STATE : SCROLL_R_STATE; + if (this.className.indexOf("disabled") != -1 + || state & SCROLL_DOWN) return; + setButtonState(dir, SCROLL_HOVER); + setButtonState(revDir, SCROLL_OFF); + globalDir = dir; + }; + _self[sBtn].onmouseout = function() { + var state = dir & SCROLL_LEFT ? SCROLL_L_STATE : SCROLL_R_STATE; + if (this.className.indexOf("disabled") != -1 + || state & SCROLL_DOWN) return; + setButtonState(dir, SCROLL_OFF); + }; + _self[sBtn].onmouseup = function() { + if (this.className.indexOf("disabled") == -1) { + setButtonState(dir, SCROLL_OFF); + } + stopTimer(); + SCROLLANIM.control.stop = true; + }; + }); + } + + + apf.layout.setRules(this.$ext, this.$uniqueId + "_tabscroller", + "var o = apf.all[" + this.$uniqueId + "]; o && o.correctScrollState()"); + apf.layout.queue(this.$ext);*/ + + + + //Skin changing support + if (this.$int) { + //apf.AmlParser.replaceNode(this.oPages, oPages); + this.$int = this.oPages; + page = true; + + //@todo apf3.0 skin change? + nodes = this.childNodes; + for (i = 0; i < nodes.length; i++) { + node = nodes[i]; + if(node.nodeType != 1) + continue; + node.$draw(true); + if(node.$skinchange) + node.$skinchange(); + node.$loadAml(); + } + } + else { + this.$int = this.oPages; + + //Build children + nodes = this.getPages(); + if (nodes.length) { + nodes[0].$first(); + (node = nodes[nodes.length - 1]).$last(); + } + } + + //Set active page + if (node) { + this.activepage = (typeof this.activepage != "undefined" + ? this.activepage + : this.activepagenr) || 0; + page = this.getPage(this.activepage); + if (!page.render || page.$rendered) + this.$propHandlers.activepage.call(this, this.activepage); + } + else { + this.isPages = false; + } + + + this.setProperty("length", this.getPages().length); + + + this.ready = true; + + /*window.setTimeout(function() { + _self.setScrollerState(); + }, 0);*/ + + + if (!this.activepage && this.getAttribute("src")) { + this.src = this.getAttribute("src"); + this.$propHandlers["activepage"].call(this); + } + }; + + this.$destroy = function(bSkinChange) { + if (bSkinChange || !this.oScroller) + return; + + + /*apf.layout.removeRule(this.$ext, this.$uniqueId + "_tabscroller"); + + [this.oLeftScroll, this.oRightScroll].forEach(function(oBtn) { + oBtn.onmousedown = oBtn.ondblclick = oBtn.onmouseover = + oBtn.onmouseout = oBtn.onmouseup = null; + });*/ + + }; +}).call(apf.BaseTab.prototype = new apf.Presentation()); + + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/core/baseclasses/basetree.js)SIZE(51603)TIME(Tue, 13 Dec 2011 13:33:58 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + + +/** + * Baseclass of elements that allows the user to select one or more items + * from a tree based element. + * + * @constructor + * @baseclass + * + * @inherits apf.XForms + * @inherits apf.Cache + * @inherits apf.DataAction + * @inherits apf.Rename + * + * @author Ruben Daniels (ruben AT ajax DOT org) + * @version %I%, %G% + * @since 0.8 + * @default_private + * + */ +apf.BaseTree = function(){ + this.$init(true); + + + this.$dynCssClasses = []; + + + this.$nodes = []; +}; + +(function() { + + this.implement( + + + apf.Rename, + + + apf.DataAction, + + + apf.Cache, + + apf.K + ); + + + /**** Properties and Attributes ****/ + + //Options + this.$isTreeArch = true; // This element has a tree architecture. + this.$focussable = true; // This object can get the focus. + this.multiselect = false; // Initially multiselect is disabled. + this.bufferselect = true; + + this.startcollapsed = true; + this.$animType = apf.tween.NORMAL; + this.$animOpenStep = 3; + this.$animCloseStep = 1; + this.$animSpeed = 10; + + var HAS_CHILD = 1 << 1, + IS_CLOSED = 1 << 2, + IS_LAST = 1 << 3, + IS_ROOT = 1 << 4; + + var treeState = this.$treeState = {}; + this.$treeState[0] = ""; + this.$treeState[HAS_CHILD] = "min"; + this.$treeState[HAS_CHILD | IS_CLOSED] = "plus"; + this.$treeState[IS_LAST] = "last"; + this.$treeState[IS_LAST | HAS_CHILD] = "minlast"; + this.$treeState[IS_LAST | HAS_CHILD | IS_CLOSED] = "pluslast"; + this.$treeState[IS_ROOT] = "root"; + + /**** Properties and Attributes ****/ + + /** + * @attribute {Boolean} openadd whether the tree expands the parent to which a node is added. Defaults to true. + * @attribute {Boolean} startcollapsed whether the tree collapses all nodes that contain children on load. Defaults to true. + * @attribute {Boolean} nocollapse whether the user cannot collapse a node. Defaults to false. + * @attribute {Boolean} singleopen whether the tree will expand a node by a single click. Defaults to false. + * @attribute {Boolean} prerender whether the tree will render all the nodes at load. Defaults to true. + * @attribute {Boolean} disableremove whether the tree disallows removing nodes with the keyboard, by pressing DEL. Defaults to false. + */ + this.$booleanProperties["openadd"] = true; + this.$booleanProperties["startcollapsed"] = true; + this.$booleanProperties["nocollapse"] = true; + this.$booleanProperties["singleopen"] = true; + this.$booleanProperties["animation"] = true; + this.$booleanProperties["prerender"] = true; + this.$booleanProperties["removecontainer"] = true; + this.$booleanProperties["dragroot"] = true; + this.$booleanProperties["disableremove"] = true; + + this.$supportedProperties.push("openadd", "startcollapsed", "nocollapse", + "singleopen", "prerender", "removecontainer", "animation", "dragroot", + "disableremove"); + + this.openadd = true; + this.startcollapsed = 1; + this.prerender = true; + this.disableremove = false; + + /**** Public Methods ****/ + + /** + * Expands all items in the tree + */ + this.expandAll = function(){ + if (!this.xmlRoot) + return; + + var xpath = this.each.split('|') + .join('[' + this.each.replace(/\|/g, " or ") + ']|.//'), + _self = this; + (function(node){ + var nodes = node.selectNodes(xpath); + //for (var i = nodes.length - 1; i >= 0; i--) { + for (var o, i = 0; i < nodes.length; i++) { + if (o = apf.xmldb.getHtmlNode(nodes[i], _self)) + _self.slideToggle(o, 1, true); + arguments.callee(nodes[i]); + } + })(this.xmlRoot); + }; + + /** + * Collapses all items in the tree + */ + this.collapseAll = function(){ + if (!this.xmlRoot) + return; + + var pNodes = this.xmlRoot.selectNodes(".//" + this.each + .split('|').join('[' + this.each.replace(/\|/g, " or ") + ']|.//')); + + for (var o, i = pNodes.length - 1; i >=0; i--) { + if (o = apf.xmldb.getHtmlNode(pNodes[i], this)) + this.slideToggle(o, 2, true); + } + }; + + /** + * Selects a node and expands each parent of it. + */ + this.expandAndSelect = function(xmlNode) { + if (!this.xmlRoot) + return; + + var _self = this; + if ((function _recur(loopNode){ + var pNode = _self.getTraverseParent(loopNode); + if (pNode == _self.xmlRoot) + return true; + + if (!pNode || _recur(pNode) === false) + return false; + + _self.slideToggle(apf.xmldb.getHtmlNode(pNode, _self), 1, true); + })(xmlNode) !== false) + this.select(xmlNode); + } + + /** + * Loads a list of folders + * paths {Array} Array of strings in the form of 'folder[1]/folder[2]' + * onFinished {function} Callback to be called when finished + */ + this.expandList = function (paths, onFinished) { + var _self = this; + var root = this.xmlRoot; + + // recursive load function + function expand(currentSelector, allSelectors) { + // first expand the item passed in + _self.slideToggle(apf.xmldb.getHtmlNode(root.selectSingleNode(currentSelector), _self), 1, true, null, function () { + // the number of times the callback has fired, prevent it from executing forever + var timesRan = 0; + + // the callback fires, but we might be waiting for data from the server + var callback = function () { + // check whether the node is loaded + if (!_self.$hasLoadStatus(root.selectSingleNode(currentSelector), "loaded")) { + // otherwise wait if timesRan under 30 + return ++timesRan < 30 ? setTimeout(callback, 1000 / 30) : null; + } + + // notify + hasExpanded(currentSelector); + + // when finished, find all the other selectors that start with the current selector + // plus a slash to make it really really sure + // plus we check whether it's a child and not a grand child + var childSelectors = allSelectors.filter(function (s) { + return s.indexOf(currentSelector + "/") === 0 + && currentSelector.split("/").length + 1 === s.split("/").length; + }); + + // then expand each of the child items + childSelectors.forEach(function (selector) { + expand(selector, allSelectors); + }); + }; + callback(); + }); + } + + // function to be called when an item has expanded, used to determine whether we finished + var expandCount = 0; + function hasExpanded(selector) { + // if we have expanded all items, invoke the callback + if (++expandCount === paths.length) { + onFinished(); + } + } + + // find all rootNodes (nodes without a slash in them) + var rootNodes = paths.filter(function (p) { return p.split("/").length === 1; }); + + // expand all root nodes, expand will recursively expand all child items + rootNodes.forEach(function (node) { + expand(node, paths); + }); + }; + + /** + * @notimplemented + * @todo who's volunteering? + * @private + */ + this.selectPath = function(path){}; + + /**** Sliding functions ****/ + + /** + * @private + */ + this.slideToggle = function(htmlNode, force, immediate, userAction, callback){ + if (this.nocollapse || userAction && this.disabled) + return; + + if (!htmlNode) + htmlNode = this.$selected; + + if (!htmlNode) + return callback && callback(); + + var id = htmlNode.getAttribute(apf.xmldb.htmlIdTag); + while (!id && htmlNode.parentNode) + id = (htmlNode = htmlNode.parentNode) + .getAttribute(apf.xmldb.htmlIdTag); + + var container = this.$getLayoutNode("item", "container", htmlNode); + if (!container) return; + + if (apf.getStyle(container, "display") == "block") { + if (force == 1) { + if (callback) callback(); + return; + } + htmlNode.className = htmlNode.className.replace(/min/, "plus"); + this.slideClose(container, apf.xmldb.getNode(htmlNode), immediate, callback); + } + else { + if (force == 2) { + if (callback) callback(); + return; + } + htmlNode.className = htmlNode.className.replace(/plus/, "min"); + this.slideOpen(container, apf.xmldb.getNode(htmlNode), immediate, callback); + } + }; + + this.isCollapsed = function(xmlNode){ + return (apf.getStyle(this.$getLayoutNode("item", "container", + apf.xmldb.getHtmlNode(xmlNode, this)), "display") == "none"); + } + + var lastOpened = {}; + /** + * @event expand Fires when a tree leaf is expanded from collapsed view to + * reveal its children leaves. + * @private + */ + this.slideOpen = function(container, xmlNode, immediate, callback){ + if (!xmlNode) + xmlNode = this.selected; + + var htmlNode = apf.xmldb.getHtmlNode(xmlNode, this); + if (!container) + container = this.$findContainer(htmlNode); + + //We don't slide open elements without children. + if ((!container.childNodes.length || container.firstChild.nodeType == 3 + && container.firstChild.nodeValue.trim() == "") + && !this.$getBindRule("insert", xmlNode) + && !this.getTraverseNodes(xmlNode).length) + return callback && callback(); + + if (this.singleopen) { + var pNode = this.getTraverseParent(xmlNode), + p = (pNode || this.xmlRoot).getAttribute(apf.xmldb.xmlIdTag); + if (lastOpened[p] && lastOpened[p][1] != xmlNode + && this.getTraverseParent(lastOpened[p][1]) == pNode) + this.slideToggle(lastOpened[p][0], 2);//lastOpened[p][1]); + lastOpened[p] = [htmlNode, xmlNode]; + } + + if (!this.nocollapse) + container.style.display = "block"; + + if (!this.prerender && this.$hasLoadStatus(xmlNode, "potential") + && !container.childNodes.length) { + this.$extend(xmlNode, container, immediate, callback); + return; + } + + if (immediate || container.scrollHeight > 1000) { + if (!this.nocollapse && container != this.$container) { + container.style.height = "auto"; + container.style.overflow = "visible"; + } + + if (this.$hasLoadStatus(xmlNode, "potential")) + return this.$extend(xmlNode, container, immediate, callback); + + this.dispatchEvent("expand", {xmlNode: xmlNode}); + return callback && callback(); + } + + var _self = this; + var prevHeight = container.style.height; + container.style.overflow = "visible"; + if (!apf.isIE7) { + container.style.height = apf.hasHeightAutoDrawBug ? "100%" : "auto"; + } + var height = container.scrollHeight; + container.style.overflow = "hidden"; + container.style.height = prevHeight; + + function finishSlide() { + if (xmlNode && _self.$hasLoadStatus(xmlNode, "potential")) { + $setTimeout(function(){ + if (container != this.$container) { + container.style.height = container.scrollHeight + "px"; + container.style.overflow = "hidden"; + } + _self.$extend(xmlNode, container, null, callback); + }); + if (container != this.$container) { + if (!apf.isIE7) { + container.style.height = apf.hasHeightAutoDrawBug ? "100%" : "auto"; + } + container.style.overflow = "visible"; + } + } + else if (container != this.$container) { + container.style.overflow = "visible"; + if (!apf.isIE7) { + container.style.height = apf.hasHeightAutoDrawBug ? "100%" : "auto"; + } + _self.dispatchEvent("expand", {xmlNode: xmlNode}); + } + } + + if(!this.getAttribute("animation")) { + var diff = apf.getHeightDiff(container), + oInt = container; + + container.style.height = Math.max((height), 0) + "px"; + oInt.scrollTop = oInt.scrollHeight - oInt.offsetHeight - diff - (apf.isGecko ? 16 : 0); + finishSlide(); + } + else { + apf.tween.single(container, { + type : 'scrollheight', + from : container.offsetHeight, + to : height, + anim : this.$animType, + steps : this.$animOpenStep, + interval: this.$animSpeed, + onfinish: function(container){ + finishSlide(); + } + }); + } + }; + + /** + * @event collapse Fires when a tree leaf is collapsed from expanded view to + * conceal its children leaves. + * @private + */ + this.slideClose = function(container, xmlNode, immediate){ + if (this.nocollapse) + return; + + if (!xmlNode) + xmlNode = this.selected; + + if (this.singleopen) { + var p = (this.getTraverseParent(xmlNode) || this.xmlRoot) + .getAttribute(apf.xmldb.xmlIdTag); + lastOpened[p] = null; + } + + if (!container) { + var htmlNode = apf.xmldb.getHtmlNode(xmlNode, this); + container = this.$findContainer(htmlNode); + } + + if (container != this.$container) { + container.style.height = container.offsetHeight; + container.style.overflow = "hidden"; + } + + if (immediate) { + if (container != this.$container) + container.style.height = 0; + container.style.display = "none"; + this.dispatchEvent("collapse", {xmlNode: xmlNode}); + return; + } + + var _self = this; + apf.tween.single(container, { + type : 'scrollheight', + from : container.scrollHeight, + to : 0, + anim : this.$animType, + steps : this.$animCloseStep, + interval: this.$animSpeed, + onfinish: function(container, data){ + container.style.display = "none"; + _self.dispatchEvent("collapse", {xmlNode: xmlNode}); + } + }); + }; + + /**** Databinding Support ****/ + + this.$isStartCollapsed = function(xmlNode){ + return this.$hasBindRule("collapsed") + ? (this.$getDataNode("collapsed", xmlNode) ? true : false) + : (this.$hasBindRule("expanded") + ? (this.$getDataNode("expanded", xmlNode) ? false : true) + : this.startcollapsed); + } + + //@todo apf3.x refactor + this.$add = function(xmlNode, Lid, xmlParentNode, htmlParentNode, beforeNode, isLast, depth, nextNode, action){ + if (this.$isTreeArch && this.$needsDepth && typeof depth == "undefined") { + var loopNode = xmlParentNode; depth = 0; + while(loopNode != this.xmlRoot) { + depth++; + loopNode = loopNode.parentNode; + } + } + + var loadChildren = this.$getBindRule("insert", xmlNode) ? true : false, + traverseNodes = this.getTraverseNodes(xmlNode), + hasTraverseNodes = traverseNodes.length ? true : false, + hasChildren = loadChildren || hasTraverseNodes, + startcollapsed = this.$isStartCollapsed(xmlNode), + state = (hasChildren ? HAS_CHILD : 0) | (startcollapsed && hasChildren + || loadChildren ? IS_CLOSED : 0) | (isLast ? IS_LAST : 0), + + htmlNode = this.$initNode(xmlNode, state, Lid, depth), + container = this.$getLayoutNode("item", "container", htmlNode), + eachLength; + + if (!startcollapsed && !this.nocollapse) + container.setAttribute("style", "overflow:visible;height:auto;display:block;"); + + var msg, removeContainer = (!this.removecontainer || hasChildren); + + //TEMP on for dynamic subloading + if (!hasChildren || loadChildren) + container.setAttribute("style", "display:none;"); + + //Dynamic SubLoading (Insertion) of SubTree + if (!this.prerender) + eachLength = traverseNodes.length; + + if (hasChildren && !this.prerender && eachLength > 2 && startcollapsed + || loadChildren && (!this.$hasLoadStatus(xmlNode) + || this.$hasLoadStatus(xmlNode, "potential"))) + this.$setLoading(xmlNode, container); + else if (!hasTraverseNodes && (msg = this.$applyBindRule("empty", xmlNode))) { + this.$setEmptyMessage(container, msg); + } + + if ((!htmlParentNode || htmlParentNode == this.$container) + && xmlParentNode == this.xmlRoot && !beforeNode + || action == "insert") { + this.$nodes.push(htmlNode); + if (!apf.isChildOf(htmlNode, container, true) && removeContainer) + this.$nodes.push(container); + + if (action != "insert") { + this.$setStyleClass(htmlNode, "root"); + this.$setStyleClass(container, "root"); + } + } + else { + if (!htmlParentNode) { + htmlParentNode = apf.xmldb.getHtmlNode(xmlNode.parentNode, this); + htmlParentNode = htmlParentNode + ? this.$getLayoutNode("item", "container", htmlParentNode) + : this.$container; + } + + if (htmlParentNode == this.$container) { + this.$setStyleClass(htmlNode, "root"); + this.$setStyleClass(container, "root"); + } + + var next; + if (action != "load" && action != "extend") { + if (!beforeNode && (next = this.getNextTraverse(xmlNode))) + beforeNode = apf.xmldb.getHtmlNode(next, this); + } + if (beforeNode && beforeNode.parentNode != htmlParentNode) + beforeNode = null; + + if (htmlParentNode.style + && this.getTraverseNodes(xmlNode.parentNode).length == 1) + this.$removeEmptyMessage(htmlParentNode); + + //alert("|" + htmlNode.nodeType + "-" + htmlParentNode.nodeType + "-" + beforeNode + ":" + container.nodeType); + //Insert Node into Tree + if (htmlParentNode.style) { + var isChildOfHtmlNode = !apf.isChildOf(htmlNode, container, true) + htmlNode = apf.insertHtmlNode(htmlNode, htmlParentNode, beforeNode); + if (isChildOfHtmlNode && removeContainer) + var container = apf.insertHtmlNode(container, + htmlParentNode, beforeNode); + else + var container = this.$getLayoutNode("item", "container", htmlNode); + } + else { + htmlParentNode.insertBefore(htmlNode, beforeNode); + if (!apf.isChildOf(htmlNode, container, true) && removeContainer) + htmlParentNode.insertBefore(container, beforeNode); + } + + //Fix parent if child is added to drawn parentNode + if (htmlParentNode.style) { + if (this.openadd && htmlParentNode != this.$container + && htmlParentNode.style.display != "block") { + if (!this.$isStartCollapsed(xmlParentNode)) + this.slideOpen(htmlParentNode, xmlParentNode, true); + } + + if (!this.$fillParent) + this.$fillParent = xmlParentNode; + + var next = nextNode == undefined ? this.getNextTraverse(xmlNode, true) : nextNode; + + var html; + if (next && (html = apf.xmldb.getHtmlNode(next, this))) //should use each here + this.$fixItem(next, html); + } + } + + if ((this.prerender || eachLength < 3 || !startcollapsed) && (xmlNode.namespaceURI != apf.ns.apf || xmlNode.localName != "item")) { + this.$addNodes(xmlNode, container, false, null, null, (depth || 0) + 1); //checkChildren ??? + } + /*else { + this.$setLoadStatus(xmlNode, "potential"); + }*/ + + return container; + }; + + this.$fill = function(){ + if (this.$useiframe) + this.$pHtmlDoc = this.oDoc; + + if (this.$nodes.length) { + apf.insertHtmlNodes(this.$nodes, this.$fillParentHtml || this.$container); + this.$nodes.length = 0; + delete this.$fillParentHtml; + } + + if (this.$fillParent) { + this.$fixItem(this.$fillParent, apf.xmldb.getHtmlNode(this.$fillParent, this)); + delete this.$fillParent; + } + }; + + this.$getParentNode = function(htmlNode){ + return htmlNode + ? this.$getLayoutNode("item", "container", htmlNode) + : this.$container; + }; + + this.$fixItem = function(xmlNode, htmlNode, isDeleting, oneLeft, noChildren){ + if (!htmlNode) return; + + if (isDeleting) { + //if isLast fix previousSibling + var prevSib; + if (prevSib = this.getNextTraverse(xmlNode, true)) + this.$fixItem(prevSib, this.$findHtmlNode(prevSib + .getAttribute(apf.xmldb.xmlIdTag) + "|" + + this.$uniqueId), null, true); + + //if no sibling fix parent + if (!this.emptyMessage && xmlNode.parentNode.selectNodes(this.each).length == 1) + this.$fixItem(xmlNode.parentNode, this.$findHtmlNode( + xmlNode.parentNode.getAttribute(apf.xmldb.xmlIdTag) + + "|" + this.$uniqueId), null, false, true); + } + else { + var container = this.$getLayoutNode("item", "container", htmlNode), + hasChildren = false; + if (noChildren) + hasChildren = false; + else if (this.getTraverseNodes(xmlNode).length > 0) + hasChildren = true; + else if (this.$hasLoadStatus(xmlNode, "potential")) + hasChildren = true; + else + hasChildren = false; + + var isClosed = hasChildren && apf.getStyle(container, "display") == "none"; //htmlNode.className.indexOf("min") == -1;//container.style.display != "block", + isLast = this.getNextTraverse(xmlNode, null, oneLeft ? 2 : 1) + ? false + : true, + state = (hasChildren ? HAS_CHILD : 0) + | (isClosed ? IS_CLOSED : 0) | (isLast ? IS_LAST : 0); + this.$setStyleClass(this.$getLayoutNode("item", "class", htmlNode), + treeState[state], ["min", "plus", "last", "minlast", "pluslast"]); + this.$setStyleClass(this.$getLayoutNode("item", "container", htmlNode), + treeState[state], ["min", "plus", "last", "minlast", "pluslast"]); + + if(this.$getLayoutNode("item", "openclose", htmlNode)) + this.$getLayoutNode("item", "openclose", htmlNode) + .setAttribute("children", hasChildren); + + if (container) { + if (!hasChildren) + container.style.display = "none"; + else if (!isClosed) + container.style.display = "block"; + } + } + }; + + this.$deInitNode = function(xmlNode, htmlNode){ + //Lookup container + var containerNode = this.$getLayoutNode("item", "container", htmlNode), + pContainer = htmlNode.parentNode; + + //Remove htmlNodes from tree + containerNode.parentNode.removeChild(containerNode); + pContainer.removeChild(htmlNode); + + //Datagrid?? + if (this.$withContainer) + htmlNode.parentNode.removeChild(htmlNode.nextSibling); + + //Fix Images (+, - and lines) + if (xmlNode.parentNode != this.xmlRoot) + this.$fixItem(xmlNode, htmlNode, true); + + var msg; + if (!pContainer.childNodes.length && (msg = this.$applyBindRule("empty", xmlNode))) + this.$setEmptyMessage(pContainer, msg); + + //Fix look (tree thing) + this.$fixItem(xmlNode, htmlNode, true); + //this.$fixItem(xmlNode.parentNode, apf.xmldb.findHtmlNode(xmlNode.parentNode, this)); + /*throw new Error(); + if(xmlNode.previousSibling) //should use each here + this.$fixItem(xmlNode.previousSibling, apf.xmldb.findHtmlNode(xmlNode.previousSibling, this));*/ + }; + + this.$moveNode = function(xmlNode, htmlNode, oldXmlParent){ + if (!self.apf.debug && !htmlNode) + return; + + var container; + if (this.$hasLoadStatus(xmlNode.parentNode, "potential")) { + container = this.$getLayoutNode("item", "container", htmlNode); + htmlNode.parentNode.removeChild(htmlNode); + container.parentNode.removeChild(container); + this.$extend(xmlNode.parentNode); + return; + } + + var oPHtmlNode = htmlNode.parentNode, + tParent = this.getTraverseParent(xmlNode), + pHtmlNode = apf.xmldb.getHtmlNode(tParent, this), + //if(!pHtmlNode) return; + + nSibling = this.getNextTraverse(xmlNode), + beforeNode = nSibling + ? apf.xmldb.getHtmlNode(nSibling, this) + : null, + pContainer = pHtmlNode + ? this.$getLayoutNode("item", "container", pHtmlNode) + : this.$container; + + container = this.$getLayoutNode("item", "container", htmlNode); + + if (pContainer != oPHtmlNode && this.getTraverseNodes(xmlNode.parentNode).length == 1) + this.$removeEmptyMessage(pContainer); + + pContainer.insertBefore(htmlNode, beforeNode); + if (container) + pContainer.insertBefore(container, beforeNode); + + /*if (!this.startcollapsed) { + pContainer.style.display = "block"; + pContainer.style.height = "auto"; + }*/ + + var msg; + if (!this.getTraverseNodes(oldXmlParent).length && (msg = this.$applyBindRule("empty", oldXmlParent))) + this.$setEmptyMessage(oPHtmlNode, msg); + +// if (this.openadd && pHtmlNode != this.$container && pContainer.style.display != "block") +// this.slideOpen(pContainer, pHtmlNode, true); + + //Fix look (tree thing) + this.$fixItem(xmlNode, htmlNode); + + this.$fixItem(tParent, apf.xmldb.getHtmlNode(tParent, this)); + this.$updateNode(oldXmlParent, apf.xmldb.getHtmlNode(oldXmlParent, this)); + var next; + if (next = this.getNextTraverse(xmlNode, true)) { //should use each here + this.$fixItem(next, apf.xmldb.getHtmlNode(next, this)); + } + }; + + //??? + this.$setLoading = function(xmlNode, container){ + this.$setLoadStatus(xmlNode, "potential"); + + var len = this.getTraverseNodes(xmlNode).length; + if (!len || len > 20) { + this.$getNewContext("loading"); + apf.insertHtmlNode(this.$getLayoutNode("loading"), container); + } + }; + + //??? + this.$removeLoading = function(xmlNode){ + if (!xmlNode) return; + + if (this.$timers) + clearTimeout(this.$timers[xmlNode.getAttribute(apf.xmldb.xmlIdTag)]); + + var htmlNode = apf.xmldb.getHtmlNode(xmlNode, this); + if (htmlNode) { + this.$getLayoutNode("item", "container", htmlNode).innerHTML = ""; + this.$setStyleClass(htmlNode, "", ["loading"]); + } + }; + + //check databinding for how this is normally implemented + this.$extend = function(xmlNode, container, immediate, callback){ + if (!this.$hasLoadStatus(xmlNode, "potential")) + return; + + var rule = this.$getBindRule("insert", xmlNode), + xmlContext = rule && rule.match + ? (rule.cmatch || rule.compile("match"))(xmlNode) + : xmlNode; + + if (rule && xmlContext) { + this.$setLoadStatus(xmlNode, "loading"); + + var _self = this; + (this.$timers || (this.$timers = {}))[xmlNode.getAttribute(apf.xmldb.xmlIdTag)] = $setTimeout(function(){; + _self.$setStyleClass(apf.xmldb.getHtmlNode(xmlNode, _self), "loading"); + }, 100); + + if (rule.get) { + + + this.getModel().$insertFrom(rule.getAttribute("get"), { + xmlNode : xmlContext, + insertPoint : xmlContext, + amlNode : this, + callback : callback + }); + } + else { + if (this.$applyBindRule("insert", xmlNode)) + this.insert(data, {insertPoint: xmlContext}); + } + } + else if (!this.prerender) { + this.$setLoadStatus(xmlNode, "loaded"); + this.$removeLoading(xmlNode); + xmlUpdateHandler.call(this, { + action : "insert", + xmlNode : xmlNode, + result : this.$addNodes(xmlNode, container, true, null, null, null, "extend"), //checkChildren ??? + anim : !immediate + }); + } + }; + + function xmlUpdateHandler(e){ + /* + Display the animation if the item added is + * Not in the cache + - Being insterted using xmlUpdate + - there is at least 1 child inserted + */ + + if (e.action == "move-away") + this.$fixItem(e.xmlNode, apf.xmldb.findHtmlNode(e.xmlNode, this), true); + + if (e.action != "insert") return; + + var htmlNode = this.$findHtmlNode(e.xmlNode.getAttribute( + apf.xmldb.xmlIdTag) + "|" + this.$uniqueId); + if (!htmlNode) return; + + //this.$hasLoadStatus(e.xmlNode, "loading") + if (e.action == "insert" && e.result.length > 0) { + if (this.$hasLoadStatus(e.xmlNode, "loaded", true)) { + var container = this.$getLayoutNode("item", "container", htmlNode); + this.slideOpen(container, e.xmlNode);//, e.$anim ? false : true + } + else if (this.$hasLoadStatus(e.xmlNode, "potential", true)) { + this.$setLoadStatus(e.xmlNode, "loaded"); + } + } + else + this.$fixItem(e.xmlNode, htmlNode); + + //Can this be removed?? (because it was added in the insert function) + //if (this.$hasLoadStatus(e.xmlNode, "loading")) + //this.$setLoadStatus(e.xmlNode, "loaded"); + } + + this.addEventListener("xmlupdate", xmlUpdateHandler); + + /**** Keyboard Support ****/ + + + this.addEventListener("beforerename", function(){ + if (this.$tempsel) { + clearTimeout(this.timer); + this.select(this.$tempsel); + + this.$tempsel = null; + this.timer = null; + } + }); + + + this.scrollIntoView = function(sNode, onTop) { + var selHtml = apf.xmldb.getHtmlNode(sNode, this), top; + if (!selHtml) + return; + + top = apf.getAbsolutePosition(selHtml, this.$container)[1]; + + if (onTop) { + if (top <= this.$container.scrollTop) + this.$container.scrollTop = top; + } + else { + if (top > this.$container.scrollTop + this.$container.offsetHeight) + this.$container.scrollTop = top - this.$container.offsetHeight + selHtml.offsetHeight; + } + } + + + this.addEventListener("keydown", function(e){ + var key = e.keyCode, + ctrlKey = e.ctrlKey, + shiftKey = e.shiftKey, + selHtml = this.$caret || this.$selected, + pos, top, el, node, nodes, sNode, pNode, container; + + if (e.returnValue == -1 || !selHtml || this.renaming) //@todo how about allowdeselect? + return; + + var selXml = this.caret || this.selected, + oExt = this.$container; + + switch (key) { + case 13: + if (this.$tempsel) + this.$selectTemp(); + + if (this.ctrlselect == "enter") + this.select(this.caret, true); + + this.choose(selHtml); + break; + case 32: + if (this.$tempsel) + this.$selectTemp(); + + + if (this.$mode && !ctrlKey) { + var sel = this.getSelection(); + if (!sel.length || !this.multiselect) + this.checkToggle(this.caret, true); + else + this.checkList(sel, this.isChecked(this.selected), true, false, true); + } + else + + if (ctrlKey || !this.isSelected(this.caret)) + this.select(this.caret, true); + return false; + case 46: + if (this.disableremove) + return; + + if (this.$tempsel) + this.$selectTemp(); + + //DELETE + //this.remove(); + this.remove(); //this.mode != "check" + break; + case 36: + //HOME + this.$setTempSelected(this.getFirstTraverseNode(), false, shiftKey); + oExt.scrollTop = 0; + return false; + case 35: + //END + var lastNode = this.getLastTraverseNode(); + while (!this.isCollapsed(lastNode)) + lastNode = this.getLastTraverseNode(lastNode); + + this.$setTempSelected(lastNode, false, shiftKey, true); + oExt.scrollTop = oExt.scrollHeight; + return false; + case 37: + //LEFT + if (this.$tempsel) + this.$selectTemp(); + if (this.caret.selectSingleNode(this.each) + && !this.isCollapsed(this.caret)) + this.slideToggle(this.$caret || this.$selected, 2) + else if ((pNode = this.getTraverseParent(this.caret)) + && pNode != this.xmlRoot) + this.select(pNode) + return false; + case 107: //+ + case 187: //+ + case 39: + //RIGHT + if (this.$tempsel) + this.$selectTemp(); + + if (this.$hasLoadStatus(this.caret, "potential") + || this.getFirstTraverseNode(this.caret)) + this.slideToggle(this.$caret || this.$selected, 1) + break; + case 109: + case 189: + //- + if (this.getFirstTraverseNode(this.caret)) + this.slideToggle(this.$caret || this.$selected, 2) + break; + case 38: + //UP + if (!selXml && !this.$tempsel) + return; + + node = this.$tempsel + ? apf.xmldb.getNode(this.$tempsel) + : selXml; + + sNode = this.getNextTraverse(node, true); + if (sNode && sNode != node) { + nodes = this.getTraverseNodes(sNode); + + do { + container = this.$getLayoutNode("item", "container", + this.$findHtmlNode(apf.xmldb.getID(sNode, this))); + if (container && apf.getStyle(container, "display") == "block" + && nodes.length) { + sNode = nodes[nodes.length-1]; + } + else { + break; + } + } + while (sNode && (nodes = this.getTraverseNodes(sNode)).length); + } + else if (this.getTraverseParent(node) == this.xmlRoot) { + this.dispatchEvent("selecttop"); + return; + } + else + sNode = this.getTraverseParent(node); + + if (sNode && sNode.nodeType == 1) + this.$setTempSelected(sNode, ctrlKey, shiftKey, true); + else + return false; + + selHtml = apf.xmldb.getHtmlNode(sNode, this); + top = apf.getAbsolutePosition(selHtml, this.$container)[1] + - (selHtml.offsetHeight); + if (top <= oExt.scrollTop) + oExt.scrollTop = top; + + return false; + case 40: + //DOWN + if (!selXml && !this.$tempsel) + return; + + node = this.$tempsel + ? apf.xmldb.getNode(this.$tempsel) + : selXml; + + sNode = this.getFirstTraverseNode(node); + if (sNode) { + container = this.$getLayoutNode("item", "container", + this.$findHtmlNode(apf.xmldb.getID(node, this))); + if (container && apf.getStyle(container, "display") != "block") + sNode = null; + } + + while (!sNode) { + pNode = this.getTraverseParent(node); + if (!pNode) break; + + var i = 0; + nodes = this.getTraverseNodes(pNode); + while (nodes[i] && nodes[i] != node) + i++; + sNode = nodes[i+1]; + node = pNode; + } + + if (sNode && sNode.nodeType == 1) + this.$setTempSelected(sNode, ctrlKey, shiftKey); + else + return false; + + selHtml = apf.xmldb.getHtmlNode(sNode, this); + top = apf.getAbsolutePosition(selHtml, this.$container)[1] + + (selHtml.offsetHeight); + if (top > oExt.scrollTop + oExt.offsetHeight) + oExt.scrollTop = top - oExt.offsetHeight; + return false; + case 33: //@todo + //PGUP + pos = apf.getAbsolutePosition(this.$container); + el = document.elementFromPoint(pos[0] + this.$container.offsetWidth + - 2, pos[1] + 2); + sNode = apf.xmldb.findXmlNode(el); + if (sNode == this.selected) { + oExt.scrollTop -= oExt.offsetHeight - apf.getHeightDiff(oExt); + el = document.elementFromPoint(pos[0] + this.$container.offsetWidth + - 2, pos[1] + 2); + sNode = apf.xmldb.findXmlNode(el); + } + this.select(sNode); + + selHtml = apf.xmldb.getHtmlNode(sNode, this); + top = apf.getAbsolutePosition(selHtml, this.$container)[1] + - (selHtml.offsetHeight); + if (top <= oExt.scrollTop) + oExt.scrollTop = top; + break; + case 34: //@todo + //PGDN + pos = apf.getAbsolutePosition(this.$container); + el = document.elementFromPoint(pos[0] + this.$container.offsetWidth + - 2, pos[1] + this.$ext.offsetHeight - 4); + sNode = apf.xmldb.findXmlNode(el); + if (sNode == this.selected) { + oExt.scrollTop += oExt.offsetHeight - apf.getHeightDiff(oExt); + el = document.elementFromPoint(pos[0] + this.$container.offsetWidth + - 2, pos[1] + this.$ext.offsetHeight - 4); + sNode = apf.xmldb.findXmlNode(el); + } + this.select(sNode); + + selHtml = apf.xmldb.getHtmlNode(sNode, this); + top = apf.getAbsolutePosition(selHtml, this.$container)[1] + + (selHtml.offsetHeight); + if (top > oExt.scrollTop + oExt.offsetHeight) + oExt.scrollTop = top - oExt.offsetHeight; + break; + default: + if (this.celledit) { + if (!ctrlKey && !e.altKey && (key > 46 && key < 112 || key > 123)) + this.startRename(null, true); + return; + } + else if (key == 65 && ctrlKey) { + this.selectAll(); + return false; + } + //@todo make this work with the sorted column + else if (this.caption || (this.bindingRules || {})["caption"]) { + if (!this.xmlRoot) return; + + //this should move to a onkeypress based function + if (!this.lookup || new Date().getTime() + - this.lookup.date.getTime() > 300) + this.lookup = { + str : "", + date : new Date() + }; + + this.lookup.str += String.fromCharCode(key); + + var nodes = this.getTraverseNodes(); //@todo start at current indicator + for (var v, i = 0; i < nodes.length; i++) { + v = this.$applyBindRule("caption", nodes[i]); + if (v && v.substr(0, this.lookup.str.length) + .toUpperCase() == this.lookup.str) { + + if (!this.isSelected(nodes[i])) + this.select(nodes[i]); + + if (selHtml) + this.$container.scrollTop = selHtml.offsetTop + - (this.$container.offsetHeight + - selHtml.offsetHeight) / 2; + return; + } + } + return; + } + break; + } + }, true); + + + /**** Rename Support ****/ + + + this.$getCaptionElement = function(){ + if (!this.$selected) return false; + var x = this.$getLayoutNode("item", "caption", this.$selected); + return x.nodeType == 1 ? x : x.parentNode; + }; + + + /**** Selection Support ****/ + /* + nodes = this.hasFeature(apf.__VIRTUALVIEWPORT__) + ? this.xmlRoot.selectNodes(this.$isTreeArch + ? this.each + : ".//" + this.each.split('|').join('|.//')) + : + */ + this.$calcSelectRange = function(xmlStartNode, xmlEndNode){ + var r = [], + f = false, + i = 0, + pNodeStart = this.getTraverseParent(xmlStartNode), + pNodeEnd = this.getTraverseParent(xmlEndNode), + nodes = this.getTraverseNodes(pNodeStart); + + for (; i < nodes.length; i++) { + if (nodes[i] == xmlStartNode) + f = true; + if (f) + r.push(nodes[i]); + if (nodes[i] == xmlEndNode) + f = false; + } + + if (!r.length || f) { + r = []; + for (f = false, i = nodes.length - 1; i >= 0; i--) { + if (nodes[i] == xmlStartNode) + f = true; + if (f) + r.push(nodes[i]); + if (nodes[i] == xmlEndNode) + f = false; + } + } + + return r; + }; + + this.$findContainer = function(htmlNode){ + return this.$getLayoutNode("item", "container", htmlNode); + }; + + this.$selectDefault = function(xmlNode){ + var firstNode = this.getFirstTraverseNode(xmlNode); + if (!firstNode) + return; + + if (this.select(firstNode, null, null, null, true)) { + return true; + } + else { + var nodes = this.getTraverseNodes(xmlNode); + for (var i = 0; i < nodes.length; i++) { + if (this.$selectDefault(nodes[i])) + return true; + } + } + }; + + this.$setEmptyMessage = function(htmlNode, msg){ + this.$getNewContext("empty"); + var xmlEmpty = this.$getLayoutNode("empty"); + if (!xmlEmpty) return; + + var empty = apf.insertHtmlNode(xmlEmpty, htmlNode); + empty.setAttribute("empty", "true"); + var caption = this.$getLayoutNode("empty", "caption", empty); + + if (caption) + apf.setNodeValue(caption, msg || ""); + + if (htmlNode.style) + this.slideOpen(htmlNode, null, true); + else if (this.nocollapse) + htmlNode.setAttribute("style", "display:inline-block;"); + else + htmlNode.setAttribute("style", "overflow:visible;height:auto;display:block;"); + + } + + this.$removeEmptyMessage = function(htmlNode){ + var cNode = htmlNode.firstElementChild || htmlNode.firstChild; + if (!cNode) + return; + + do { + if (cNode.getAttribute && cNode.getAttribute("empty")) { //@todo hack + htmlNode.removeChild(cNode); + return; + } + cNode = cNode.nextSibling; + } + while(cNode); + } + + /**** Init ****/ + + /** + * @event click Fires when the user presses a mousebutton while over this + * element and then let's the mousebutton go. + * @see baseclass.multiselect.event.beforeselect + * @see baseclass.multiselect.event.afterselect + * @see baseclass.multiselect.event.beforechoose + * @see baseclass.multiselect.event.afterchoose + */ + this.$drawBase = function(){ + //@todo apf3.0 checkmode, radiomode + /*if (!this.getAttribute("skin")) { + var mode = this.getAttribute("mode"); + this.skinName = null; + this.skin = mode + "tree"; + this.$loadSkin(); + }*/ + + //Build Main Skin + this.$ext = this.$getExternal(); + this.$container = this.$getLayoutNode("main", "container", this.$ext); + this.opencloseaction = this.$getOption("main", "openclose"); + + //Need fix... + //this.$ext.style.MozUserSelect = "none"; + + if (apf.hasCssUpdateScrollbarBug && !this.mode) + this.$fixScrollBug(); + + var _self = this; + this.$ext.onclick = function(e){ + _self.dispatchEvent("click", {htmlEvent : e || event}); + }; + this.$ext.onmousedown = function(e){ + _self.dispatchEvent("mousedown", {htmlEvent : e || event}); + }; + this.$ext.onmouseover = function(e){ + _self.dispatchEvent("mouseover", {htmlEvent : e || event}); + }; + this.$ext.onmouseout = function(e){ + _self.dispatchEvent("mouseout", {htmlEvent : e || event}); + }; + this.$ext.onmousemove = function(e){ + _self.dispatchEvent("mousemove", {htmlEvent : e || event}); + }; + }; + + this.addEventListener("DOMNodeInsertedIntoDocument", function(){ + if (this.nocollapse) + this.startcollapsed = false; + else if (this.startcollapsed === 1) + this.startcollapsed = !apf.isFalse(this.$getOption("main", "startcollapsed")); + }); + + this.addEventListener("DOMNodeRemovedFromDocument", function(){ + this.$ext.onclick = null; + + apf.destroyHtmlNode(this.oDrag); + this.oDrag = null; + }); + +}).call(apf.BaseTree.prototype = new apf.MultiSelect()); + + + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/core/baseclasses/delayedrender.js)SIZE(5249)TIME(Tue, 13 Dec 2011 13:33:58 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + +apf.__DELAYEDRENDER__ = 1 << 11 + + + +/** + * All elements inheriting from this {@link term.baseclass baseclass} have delayed + * rendering features. Any element that is (partially) hidden at startup has the + * possibility to delay rendering it's childNodes by setting render="runtime" on + * the element. These elements include window, tab, pages, form and container. + * For instance a Tab page in a container is initally hidden and does not + * need to be rendered. When the tab button is pressed to activate the page + * the page is rendered and then displayed. This can dramatically decrease + * the startup time of the application. + * Example: + * In this example the button isn't rendered until the advanced tab becomes active. + * + * + * + * ... + * + * + * OK + * + * + * + * + * @event beforerender Fires before elements are rendered. Use this event to display a loader. + * cancelable: Prevents rendering of the childNodes + * @event afterrender Fires after elements are rendered. User this event to hide a loader. + * + * @attribute {String} render when the contents of this element is rendered. + * Possible values: + * init elements are rendered during init of the application. + * runtime elements are rendered when the user requests them. + * @attribute {Boolean} use-render-delay whether there's a short delay between showing this element and rendering it's contents. + * Possible values: + * true The elements are rendered immediately + * false There is a delay between showing this element and the actual rendering, + * allowing the browsers' render engine to draw (for instance a loader). + * + * @constructor + * @baseclass + * @author Ruben Daniels (ruben AT ajax DOT org) + * @version %I%, %G% + * @since 0.8.9 + */ +apf.DelayedRender = function(){ + this.$regbase = this.$regbase | apf.__DELAYEDRENDER__; + this.$rendered = false; + + /** + * Renders the children of this element. + * + * @param {Boolean} [usedelay] whether a delay is added between calling + * this function and the actual rendering. This allows the browsers' + * render engine to draw (for instance a loader). + */ + this.$render = function(usedelay){ + if (this.$rendered) + return; + + if (this.dispatchEvent("beforerender") === false) + return; + + if (this["render-delay"] || usedelay) + $setTimeout("apf.lookup(" + this.$uniqueId + ").$renderparse()", 10); + else + this.$renderparse(); + }; + + this.$renderparse = function(){ + if (this.$rendered) + return; + + // Hide render pass from sight for inner callstack + // redrawing browsers like firefox + this.$ext.style.visibility = "hidden"; + + var domParser = this.ownerDocument.$domParser; + domParser.parseFromXml(this.$aml, { + amlNode : this, + doc : this.ownerDocument, + //nodelay : true, + delayedRender : true + }); + domParser.$continueParsing(this); + + this.$rendered = true; + + this.dispatchEvent("afterrender"); + + this.$ext.style.visibility = ""; + }; + + /*var _self = this; + if (apf.window.vManager.check(this, "delayedrender", function(){ + _self.$render(); + })) this.$render();*/ + + var f; + this.addEventListener("prop.visible", f = function(){ + if (arguments[0].value) { + + this.$render(); + + + this.removeEventListener("prop.visible", f); + } + }); +}; + +apf.GuiElement.propHandlers["render"] = function(value) { + if (!this.hasFeature(apf.__DELAYEDRENDER__) && value == "runtime") { + this.implement(apf.DelayedRender); + + if (this.localName != "page") { + this.visible = false; + this.$ext.style.display = "none"; + } + + if (typeof this["render-delay"] == "undefined") + this.$setInheritedAttribute("render-delay"); + } +}; + +apf.config.$inheritProperties["render-delay"] = 1; + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/core/baseclasses/dragdrop.js)SIZE(56093)TIME(Tue, 13 Dec 2011 13:33:58 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + +apf.__DRAGDROP__ = 1 << 5; + + + +/** + * All elements inheriting from this {@link term.baseclass baseclass} have drag&drop + * features. This baseclass operates on the bound data of this element. + * When a rendered item is dragged and dropped the bound data is moved or + * copied from one element to another, or to the same element but at a different + * position. This is possible because the rendered item has a + * {@link term.smartbinding bidirectional connection} to the data. Drag&drop can + * be turned on with a simple boolean, or by specifying detailed rules to set + * which data can be dragged and dropped and where. + * + * Example: + * This is a simple example, enabling drag&drop for a list. + * + * + * + * + * Example: + * This example shows a smartbinding that represents files and folders. It uses + * {@link term.datainstruction data instruction} to tell communicat to the webdav + * server when an item is copied or moved. + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * @event dragdata Fires before a drag&drop operation is started to determine the data that is dragged. + * object: + * {XMLElement} data the default data for the drag&drop operation + * @event dragstart Fires before a drag operation is started. + * cancelable: Prevents the drag operation to start. + * object: + * {XMLElement} data the data for the drag&drop operation + * {XMLElement} selection the selection at the start of the drag operation + * {HTMLElement} indicator the html element that is shown while dragging the data + * {AMLElement} host the aml source element. + * @event dragover Fires when the users drags over this aml element. + * cancelable: Prevents the possibility to drop. + * object: + * {XMLElement} data the data for the drag&drop operation + * {XMLElement} selection the selection at the start of the drag operation + * {HTMLElement} indicator the html element that is shown while dragging the data + * {AMLElement} host the aml source element. + * @event dragout Fires when the user moves away from this aml element. + * object: + * {XMLElement} data the data for the drag&drop operation + * {XMLElement} selection the selection at the start of the drag operation + * {HTMLElement} indicator the html element that is shown while dragging the data + * {AMLElement} host the aml source element. + * @event dragdrop Fires when the user drops an item on this aml element. + * cancelable: Prevents the possibility to drop. + * object: + * {XMLElement} data the data for the drag&drop operation + * {XMLElement} selection the selection at the start of the drag operation + * {HTMLElement} indicator the html element that is shown while dragging the data + * {AMLElement} host the aml source element. + * {Boolean} candrop whether the data can be inserted at the point hovered over by the user + * + * @see element.drag + * @see element.drop + * @see element.dragdrop + * + * @define dragdrop + * @allowchild drop, drag + * @define drag Determines whether a {@link term.datanode data node} can + * be dragged from this element. + * Example: + * This example shows a small mail application. The tree element displays a root + * node, accounts and folders in a tree. The datagrid contains the mails. This + * rule specifies which data nodes can be dropped where. Folders can be dropped + * in folders and accounts. Mails can be dropped in folders. + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * @attribute {String} match an xpath statement querying the + * {@link term.datanode data node} that is + * dragged. If the query matches a node it + * is allowed to be dropped. The xpath is + * automatically prefixed by 'self::'. + * @attribute {String} copy a javascript expression that determines + * whether the dragged element is a copy or + * a move. Use event.ctrlKey to use the Ctrl + * key to determine whether the element is copied. + * + * @define drop Determines whether a {@link term.datanode data node} can + * be dropped on a data node bound to this element. + * + * @attribute {String} match an xpath statement querying the + * {@link term.datanode data node} that is + * dragged. If the query matches a node it + * is allowed to be dropped. The xpath is + * automatically prefixed by 'self::'. + * @attribute {String} target an xpath statement determining the new + * parent of the dropped {@link term.datanode data node}. + * The xpath is automatically prefixed by 'self::'. + * @attribute {String} action the action to perform when the + * {@link term.datanode data node} is inserted. + * Possible values: + * tree-append Appends the {@link term.datanode data node} to the element it's dropped on. + * list-append Appends the {@link term.datanode data node} to the root element of this element. + * insert-before Inserts the {@link term.datanode data node} before the elements it's dropped on. + * @attribute {String} copy a javascript expression that determines + * whether the drop is a copy or a move. + * Use event.ctrlKey to use the Ctrl key to + * determine whether the element is copied. + */ +/** + * @constructor + * @baseclass + * @author Ruben Daniels (ruben AT ajax DOT org) + * @version %I%, %G% + * @since 0.5 + */ +apf.DragDrop = function(){ + this.$regbase = this.$regbase | apf.__DRAGDROP__; + + this.$dragInited = false; + + /* ********************** + Actions + ***********************/ + + /** + * Copies a {@link term.datanode data node} to the bound data of this element. + * + * @action + * @param {XMLElement} xmlNode the {@link term.datanode data node} which is copied. + * @param {XMLElement} pNode the new parent element of the copied + * {@link term.datanode data node}. If none + * specified the root element of the data + * loaded in this element is used. + * @param {XMLElement} [beforeNode] the position where the {@link term.datanode data node} + * is inserted. + */ + this.copy = function(nodeList, pNode, beforeNode, isMove){ + if (nodeList.nodeType) + nodeList = [nodeList]; + + var exec, + changes = [], + i = 0, + l = nodeList.length; + for (; i < l; i++) { + changes.push({ + action : isMove ? "moveNode" : "appendChild", + args : [pNode, isMove + ? nodeList[i] + : nodeList[i] = nodeList[i].cloneNode(true), beforeNode] + }); + } + + if (this.$actions[(isMove ? "movegroup" : "copygroup")]) { + exec = this.$executeAction("multicall", changes, + (isMove ? "movegroup" : "copygroup"), nodeList[0]); + } + else { + exec = this.$executeAction("multicall", changes, + (isMove ? "move" : "copy"), nodeList[0], null, null, + nodeList.length > 1 ? nodeList : null); + } + + if (exec !== false) + return nodeList; + + return false; + }; + + /** + * Moves a {@link term.datanode data node} to the bound data of this element. + * + * @action + * @param {XMLElement} xmlNode the {@link term.datanode data node} which is copied. + * @param {XMLElement} pNode the new parent element of the moved + * {@link term.datanode data node}. If none + * specified the root element of the data + * loaded in this element is used. + * @param {XMLElement} [beforeNode] the position where the + * {@link term.datanode data node} is inserted. + */ + this.move = function(nodeList, pNode, beforeNode){ + return this.copy(nodeList, pNode, beforeNode, true); + }; + + /** + * Determines whether the user is allowed to drag the passed + * {@link term.datanode data node}. The decision is made based on the + * {@link element.drag drag} and {@link element.drag drag} + * rules. These elements determine when a data node can be dropped on + * another data node. For instance, imagine a mail application with a root + * node, accounts and folders in a tree, and mails in a datagrid. The rules + * would specify you can drag&drop folders within an account, and emails between + * folders, but not on accounts or the root. + * + * @param {XMLElement} dataNode the {@link term.datanode data node} subject to the test. + * @return {Boolean} result of the test + * @see baseclass.dragdrop.method.isDragAllowed + */ + this.isDragAllowed = function(x, data){ + + + if(!this.dragroot && this.xmlRoot.firstChild == x[0]) + return false; + + if (this.disabled || !x || !x.length || !x[0]) + return false; + + if (this.drag || this.dragcopy) { + if (data) + data.merge(x); + return true; + } + + /*var rules = this.$bindings["drag"] + || this.$attrBindings && this.$attrBindings["drag"]; + if (!rules || !rules.length) + return false;*/ + + var d, + ruleList = [], + j = 0, + l = x.length; + for (; j < l; j++) { + d = this.$getDataNode("drag", x[j], null, ruleList); + if (!d) return false; //It's all or nothing + if (data) + data.push(d); + } + + return ruleList.length ? ruleList : false; + }; + + /** + * Determines whether the user is allowed to dropped the passed + * {@link term.datanode data node}. The decision is made based on the + * {@link element.drag drag} and {@link element.drag drag} + * rules. These elements determine when a data node can be dropped on + * another data node. For instance, imagine a mail application with a root + * node, accounts and folders in a tree, and mails in a datagrid. The rules + * would specify you can drag&drop folders within an account, and emails between + * folders, but not on accounts or the root. + * + * @param {XMLElement} dataNode the {@link term.datanode data node} subject + * to the test. + * @param {XMLElement} target the {@link term.datanode data node} on which + * the dragged data node is dropped. + * @return {Boolean} result of the test + * @see baseclass.dragdrop.method.isDragAllowed + */ + this.isDropAllowed = function(x, target){ + + + if (this.disabled || !x || !x.length || !target) //!x[0] ??? + return false; + + if(!this.dragroot == false && this.xmlRoot.firstChild == x[0]) + return false; + + var data, tgt, hasDropRule = this.$attrBindings && this.$attrBindings["drop"]; + if (this.drop && (!hasDropRule || hasDropRule.value == "true")) { + this.$setDynamicProperty("drop", this.hasFeature(apf.__MULTISELECT__) + ? "[" + this.each + "]" + : "[node()]"); //@todo apf3.0 make sure each is without {} + hasDropRule = true; + } + + if (hasDropRule) { + for (var j = 0, l = x.length; j < l; j++) { + data = this.$getDataNode("drop", x[j]); + if (!data) + break; + } + if (j == l && target && !apf.isChildOf(data, target, true)) + return [target, null]; + } + + var rules = this.$bindings["drop"]; + if (!rules || !rules.length) + return false; + + //@todo this can be optimized when needed + var rule, strTgt, + i = 0, + rl = rules.length; + for (; i < rl; i++) { + rule = this.$bindings.getRuleIndex("drop", i); + + for (var j = 0, l = x.length; j < l; j++) { + data = rule.cvalue ? rule.cvalue(x[j]) : rule.cmatch(x[j]); + if (!data) + break; + } + if (j != l) + continue; + + strTgt = rule.target;//node.getAttribute("target"); + if (!strTgt || strTgt == ".") { + //op = node.getAttribute("action") + //|| (this.$isTreeArch ? "tree-append" : "list-append"); + tgt = target;/*(op == "list-append" || target == this.xmlRoot + ? this.xmlRoot + : null);*/ + } + else { + tgt = (rule.ctarget || rule.compile("target"))(target); + } + + if (tgt && !apf.isChildOf(data, tgt, true)) + return [tgt, rule]; + } + + return false; + }; + + this.$dragDrop = function(xmlReceiver, xmlNodeList, rule, defaction, isParent, srcRule, event, forceCopy){ + // @todo apf3.0 action not known here yet... should be moved down? + if (action == "tree-append" && isParent) + return false; + + /* + Possibilities: + + tree-append [default]: xmlNode.appendChild(movedNode); + list-append : xmlNode.parentNode.appendChild(movedNode); + insert-before : xmlNode.parentNode.insertBefore(movedNode, xmlNode); + */ + var action = rule && rule.action;//node && node.getAttribute("action"); + + if (action) + action = (rule.caction || rule.compile("action"))(xmlNodeList[0]); + else + action = defaction; + + if (!event) + event = {}; + + //copy convenience variables + var context = { + internal : apf.DragServer.dragdata && apf.DragServer.dragdata.host == this, + ctrlKey : event.ctrlKey, + keyCode : event.keyCode + }, + //@todo apf3.0 below should actually be compileNode with with_options + ifcopy = rule && rule.copy;//.getAttribute("copy"); + + if (typeof forceCopy == "boolean") + ifcopy = forceCopy; + else if (ifcopy) { + ifcopy = !apf.isFalse((rule.ccopy || rule.compile("copy"))(xmlNodeList[0], context)); + } + else if (typeof this.dragcopy == "boolean" || typeof this.dropcopy == "boolean") { //@todo apf3.0 boolean here? + if (this.dropcopy) { + ifcopy = this.dropcopy; + } + else if (this.dragcopy) { + ifcopy = event.ctrlKey; + } + else { + //@todo read this from src + var copyRule = this.$attrBindings && this.$attrBindings["dragcopy"]; + if (copyRule) { + ifcopy = !apf.isFalse((copyRule.cvalue2 + || copyRule.compile("value", { + withopt : true + }))(xmlNodeList[0], context)); + } + } + } + + if (!ifcopy && srcRule) { //Implemented one copy is all copy + for (var i = 0, l = srcRule.length; i < l; i++) { + ifcopy = typeof srcRule[i] == "object" && srcRule[i].copy + ? !apf.isFalse((srcRule[i].ccopy || srcRule[i].compile("copy"))(xmlNodeList[0], context)) + : event.ctrlKey; + if (ifcopy) break; + } + } + + var sNode, + actRule = ifcopy ? "copy" : "move", + parentXpath = rule ? rule.getAttribute("parent") : null; //@todo apf3.0 Should be lm syntax + switch (action) { + case "list-append": + xmlReceiver = (isParent + ? xmlReceiver + : this.getTraverseParent(xmlReceiver)); + if (parentXpath) { + if (xmlReceiver.selectSingleNode(parentXpath)) + xmlReceiver = xmlReceiver.selectSingleNode(parentXpath); + else { + xmlReceiver.appendChild(xmlReceiver.ownerDocument.createElement(parentXpath)); + xmlReceiver = xmlReceiver.selectSingleNode(parentXpath); + } + } + sNode = this[actRule](xmlNodeList, xmlReceiver); + break; + case "insert-before": + sNode = isParent + ? this[actRule](xmlNodeList, xmlReceiver) + : this[actRule](xmlNodeList, xmlReceiver.parentNode, xmlReceiver); + break; + case "tree-append": + if (parentXpath) { + if (xmlReceiver.selectSingleNode(parentXpath)) + xmlReceiver = xmlReceiver.selectSingleNode(parentXpath); + else { + xmlReceiver.appendChild(xmlReceiver.ownerDocument.createElement(parentXpath)); + xmlReceiver = xmlReceiver.selectSingleNode(parentXpath); + } + } + sNode = this[actRule](xmlNodeList, xmlReceiver); + break; + } + + if (this.selectable && sNode) { + this.selectList(sNode);//, null, null, null, true); + this.setCaret(sNode[0]); + this.focus(); + } + + return sNode; + }; + + /* ********************** + Init + ***********************/ + + /** + * Loads the dragdrop rules from the dragdrop element + * + * @param {Array} rules the rules array created using {@link core.apf.method.getrules} + * @param {XMLElement} [node] the reference to the dragdrop element + * @see SmartBinding + * @private + */ + this.enableDragDrop = function(){ + + + //Set cursors + //SHOULD come from skin + this.icoAllowed = "";//this.xmlDragDrop.getAttribute("allowed"); + this.icoDenied = "";//this.xmlDragDrop.getAttribute("denied"); + + //Setup External Object + this.$ext.dragdrop = false; + + var _self = this; + + this.$ext[apf.isIphone ? "ontouchstart" : "onmousedown"] = function(e){ + if (_self.disabled) + return; + + e = e || window.event; + + + var fEl, + srcEl = e.originalTarget || e.srcElement || e.target, + multiselect = _self.hasFeature(apf.__MULTISELECT__); + if (multiselect && srcEl == _self.$container) + return; + _self.dragging = 0; + + try{ //Firefox can crash here because of some chrome permission issue + if (!apf.isIphone && _self.allowdeselect + && (srcEl == this || srcEl.getAttribute(apf.xmldb.htmlIdTag) + && _self.$getLayoutNode("item", "select", this) != this)) + return; //This broke making a selection with the mouse in rename: _self.clearSelection(); //@todo hacky - should detect what element has the select from the skin + }catch(e) {return;} + + //MultiSelect must have carret behaviour AND deselect at clicking white + if (_self.$findValueNode) + fEl = _self.$findValueNode(srcEl); + var el = (fEl + ? apf.xmldb.getNode(fEl) + : apf.xmldb.findXmlNode(srcEl)); + if (multiselect && (!_self.selected || !el || el == _self.xmlRoot)) + return; + + if (_self.isDragAllowed(multiselect ? _self.$getSelection() : el)) { + + + apf.DragServer.start(_self, srcEl, e); + } + + //e.cancelBubble = true; + }; + + this.$ext[apf.isIphone ? "ontouchmove" : "onmousemove"] = function(e){ + if (this.host.dragging != 1 || _self.disabled) return; + }; + + + { + this.$ext.onmouseup = function(){ + if (_self.disabled) + return; + + this.host.dragging = 0; + }; + + this.$ext.ondragcopy = + this.$ext.ondragstart = function(){return false;}; + } + + if (document.elementFromPointAdd) + document.elementFromPointAdd(this.$ext); + + if (this.$initDragDrop && !this.$dragInited) { + this.$initDragDrop(); + this.$dragInited = 2; + } + else { + this.$dragInited = true; + } + }; + + function disableDragDrop(){ + this.$dragInited = false; //@todo solve oExt event conflicts + + + { + this.$ext.onmousedown = this.$ext.onmousemove + = this.$ext.onmouseup = null; + } + + if (document.elementFromPointRemove) + document.elementFromPointRemove(this.$ext); + } + + this.implement( + + this.hasFeature(apf.__MULTISELECT__) + ? apf.MultiselectDragDrop : + + apf.StandardDragDrop); + + //this.$booleanProperties["drag"] = true; + //this.$booleanProperties["dragcopy"] = true; + this.$supportedProperties.push("drop", "drag", "dragcopy"); + + /** + * @attribute {Boolean} drag whether the element allows dragging of it's items. + * Example: + * + * + * item 1 + * item 2 + * item 3 + * + * + * @attribute {Boolean} dragcopy whether dragged items are copied. + * Example: + * + * + * + * + * + * + * + * + * + * + * + * Example: + * Items are only copied when the user holds the Ctrl key + * + * + * item 1 + * item 2 + * item 3 + * + * + * @attribute {Boolean} drop whether the element allows items to be dropped. + * Example: + * + * + * item 1 + * item 2 + * item 3 + * + * + * @attribute {String} dragdrop the name of the dragdrop element for this element. + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + */ + this.$propHandlers["dragcopy"] = + this.$propHandlers["dropcopy"] = + this.$propHandlers["drag"] = + this.$propHandlers["drop"] = function(value, prop){ + this[prop] = apf.isTrue(value); + + if (this.$dragInited && prop == "drag" && value && this.$dragInited != 2) { + this.$initDragDrop(); + this.$dragInited = 2; + return; + } + + if (prop == "dragcopy" || prop == "dropcopy") + return; + + if (!value && !this.drag && !this.drop && !this.$bindings + && (this.$attrBindings && (!this.$attrBindings["drag"] || !this.$attrBindings["drop"]))) + disableDragDrop.call(this); + else if (value && !this.$dragInited) + this.enableDragDrop(); + }; + + this.addEventListener("DOMNodeRemovedFromDocument", function(e){ + disableDragDrop.call(this); + + if (this.oDrag) { + apf.destroyHtmlNode(this.oDrag); + this.oDrag = null; + } + }); +}; + +apf.GuiElement.propHandlers["dragcopy"] = +apf.GuiElement.propHandlers["dropcopy"] = +apf.GuiElement.propHandlers["drop"] = +apf.GuiElement.propHandlers["drag"] = function(value, prop) { + if (!apf.isFalse(value)) { + if (!this.hasFeature(apf.__DRAGDROP__)) { + this.implement(apf.DragDrop); + this.enableDragDrop(); + } + + this[prop] = apf.isTrue(value); + } +}; + +/** + * Central object for dragdrop handling. + * @private + */ +apf.DragServer = { + Init : function(){ + + + apf.addEventListener("hotkey", function(e){ + if (apf.window.dragging && e.keyCode == 27) { + if (document.body.lastHost && document.body.lastHost.dragOut) + document.body.lastHost.dragOut(apf.dragHost); + + return apf.DragServer.stopdrag(); + } + }); + }, + + start : function(amlNode, srcEl, e, customNode){ + if (document.elementFromPointReset) + document.elementFromPointReset(); + + amlNode.dragging = 1; + + var d = window.document; + d = (!d.compatMode || d.compatMode == "CSS1Compat") + ? d.html || d.documentElement + : d.body + + var scrollX = (apf.isIE ? d.scrollLeft : window.pageXOffset), + scrollY = (apf.isIE ? d.scrollTop : window.pageYOffset), + oParent = amlNode.$ext.offsetParent, + pos + while (oParent && oParent != d && oParent.tagName != "BODY") { + scrollX -= oParent.scrollLeft; + scrollY -= oParent.scrollTop; + oParent = oParent.offsetParent; + } + + //The coordinates need to be relative to the html element that + //represents the xml data node. + if (!srcEl && customNode) { + pos = [0, 0]; + } + else { + var loopEl = srcEl, lastId; + while (loopEl && loopEl.nodeType == 1 + && !(lastId = loopEl.getAttribute(apf.xmldb.htmlIdTag))) { + loopEl = loopEl.parentNode; + } + if (!lastId) + return; + pos = apf.getAbsolutePosition(loopEl); + } + + //Set coordinates object + apf.DragServer.coordinates = { + srcElement : srcEl, + doc : d, + scrollX : scrollX, + scrollY : scrollY, + offsetX : e.clientX - pos[0], + offsetY : e.clientY - pos[1], + clientX : e.pageX ? e.pageX - window.pageXOffset : e.clientX, + clientY : e.pageY ? e.pageY - window.pageYOffset : e.clientY + }; + + //Create Drag Data Object + var selection = customNode || amlNode.hasFeature(apf.__MULTISELECT__) + ? amlNode.getSelection() + : [amlNode.xmlRoot], + data = [], + srcRules = amlNode.isDragAllowed(selection, data); + if (!srcRules) return; + + if (amlNode.hasEventListener("dragdata")) + data = amlNode.dispatchEvent("dragdata", {data : data}); + + /*for(var i = 0, l = data.length; i < l; i++) { + data[i] = apf.getCleanCopy(data[i]); + }*/ + + this.dragdata = { + rules : srcRules, + selection : selection, + data : data, + indicator : amlNode.$showDragIndicator(selection, this.coordinates), + host : amlNode + }; + + //EVENT - cancelable: ondragstart + if (amlNode.dispatchEvent("dragstart", this.dragdata) === false) + return false;//(this.amlNode.$tempsel ? select(this.amlNode.$tempsel) : false); + + amlNode.dragging = 2; + + apf.dragMode = true; + document.onmousemove = this.onmousemove; + document.onmouseup = this.onmouseup; + }, + + stop : function(runEvent, success, e){ + if (this.last) this.dragout(); + + this.dragdata.host.dispatchEvent("dragstop", apf.extend(this.dragdata, { + success: success + })); + + //Reset Objects + this.dragdata.host.dragging = 0; + this.dragdata.host.$hideDragIndicator(success); + + /*if (runEvent && this.dragdata.host.$dragstop) + this.dragdata.host.$dragstop();*/ + + apf.dragMode = false; + document.onmousemove = + document.onmouseup = null; + + this.dragdata = null; + }, + + dragover : function(o, el, e){ + var _self = this, + originalEl = el; + + function checkPermission(targetEl) { + return o.isDropAllowed && o.xmlRoot + ? o.isDropAllowed(_self.dragdata.data, targetEl) + : apf.isTrue(apf.getInheritedAttribute(o, "", function(p){ + if (p.drop) { + o = p; + if (o == apf.DragServer.last) + return false; + return true; + } + })); + } + + e = e || window.event; + + //@todo optimize by not checking the same node dragged over twice in a row + var fEl; + if (o.$findValueNode) + fEl = o.$findValueNode(el); + + if (this.lastFel && this.lastFel == fEl + || !this.lastFel && this.last == o) //optimization + return; + + //Check Permission + var elSel = (fEl + ? apf.xmldb.getNode(fEl) + : apf.xmldb.findXmlNode(el)), + candrop = checkPermission(elSel || o.xmlRoot); + + if (this.last && this.last != o) + this.dragout(this.last, e); + + this.last = o; + this.lastFel = fEl; + + if (!candrop) { + if (o && o.$dragover) { + var parentNode = (elSel || o.xmlRoot).parentNode; + if(parentNode && (el = apf.xmldb.findHtmlNode(parentNode, o))) { + if (o.$findValueNode) + fEl = o.$findValueNode(el); + + elSel = (fEl + ? apf.xmldb.getNode(fEl) + : apf.xmldb.findXmlNode(el)); + + candrop = checkPermission(parentNode); + this.lastFel = el; + + + if(!candrop) + return; + } + else + return; + } + else + return; + } + + //EVENT - cancelable: ondragover + if (o.dispatchEvent("dragover", this.dragdata, { + target : (elSel || o.xmlRoot), + lastEl : o.lastel, + originalEl : originalEl + }) === false) + candrop = false; + + //Set Cursor + var srcEl = e.originalTarget || e.srcElement || e.target; + /*srcEl.style.cursor = (candrop ? o.icoAllowed : o.icoDenied); + if (srcEl.onmouseout != this.m_out) { + srcEl.$onmouseout = srcEl.onmouseout; + srcEl.onmouseout = this.m_out; + } + o.$ext.style.cursor = (candrop ? o.icoAllowed : o.icoDenied);*/ + + //REQUIRED INTERFACE: __dragover() + if (o && o.$dragover) + o.$dragover(el, this.dragdata, candrop); + }, + + dragout : function(o, e){ + //if (this.last == o) + //return false; + + this.lastFel = null; + + //EVENT: ondragout + if (o) { + this.dragdata.htmlEvent = e; + o.dispatchEvent("dragout", this.dragdata); + } + + //REQUIRED INTERFACE: __dragout() + if (this.last && this.last.$dragout) + this.last.$dragout(null, this.dragdata); + + //Reset Cursor + //o.$ext.style.cursor = "default"; + this.last = null; + }, + + dragdrop : function(o, el, srcO, e){ + var _self = this; + + function checkPermission(targetEl) { + return o.isDropAllowed && o.xmlRoot + ? o.isDropAllowed(_self.dragdata.data, targetEl) + : apf.isTrue(apf.getInheritedAttribute(o, "", function(p){ + if (p.drop) { + o = p; + return true; + } + })); + } + + //Check Permission + var isParent, lastTop, + elSel = (o.$findValueNode + ? apf.xmldb.getNode(o.$findValueNode(el)) + : apf.xmldb.findXmlNode(el)), + candrop = checkPermission(elSel || o.xmlRoot); + + if (this.dragdata.indicator) { + lastTop = this.dragdata.indicator.style.top; + this.dragdata.indicator.style.top = "10000px"; + } + + if (!candrop) { + if (o && o.$dragover) { + var parentNode = (elSel || o.xmlRoot).parentNode, + htmlParentNode; + if(parentNode && (htmlParentNode = apf.xmldb.findHtmlNode(parentNode, o))) { + candrop = checkPermission(parentNode); + el = htmlParentNode; + } + } + } + + //EVENT - cancelable: ondragdrop + if (candrop) { + if (o.dispatchEvent("dragdrop", apf.extend({candrop : candrop, htmlEvent : e, top: lastTop}, + this.dragdata)) === false) { + candrop = false; + } + else { + if (!o.xmlRoot) { + var m = o.getModel + ? o.getModel(true) + : + + apf.nameserver.get("model", o.model) + + if (m) + m.load(this.dragdata.data[0]) + //else warn?? + return true; + } + else { + var action = candrop[1] + && candrop[1].action + || (o.$isTreeArch ? "tree-append" : "list-append"); + if (action == "list-append" && (!o.$isTreeArch && o == this.dragdata.host)) + candrop = false; + } + } + } + + if (this.dragdata.indicator) + this.dragdata.indicator.style.top = lastTop; + + //Exit if not allowed + if (!candrop) { + this.dragout(o, e); + return false; + } + + if (o.$dragDrop) { + //Move XML + var rNode = o.$dragDrop(candrop[0], this.dragdata.data, candrop[1], + action, isParent || candrop[0] == o.xmlRoot, this.dragdata.rules, e); + this.dragdata.resultNode = rNode; + } + + if (o.$dragdrop) { + o.$dragdrop(el, apf.extend({ + htmlEvent : e, + xmlNode : rNode + }, this.dragdata), candrop); + } + + //Reset Cursor + //o.$ext.style.cursor = "default"; + this.last = null; + this.lastFel = null; + + return true; + }, + + /* ********************** + Mouse Movements + ***********************/ + + onmousemove : function(e){ + if (!apf.DragServer.dragdata) return; + e = e || window.event; + + + var dragdata = apf.DragServer.dragdata, + c = { + clientX: e.pageX ? e.pageX - window.pageXOffset : e.clientX, + clientY: e.pageY ? e.pageY - window.pageYOffset : e.clientY + }; + + if (!dragdata.started + && Math.abs(apf.DragServer.coordinates.clientX - c.clientX) < 6 + && Math.abs(apf.DragServer.coordinates.clientY - c.clientY) < 6) + return; + + if (!dragdata.started) { + if (dragdata.host.$dragstart) + dragdata.host.$dragstart(null, dragdata); + dragdata.started = true; + } + + //dragdata.indicator.style.top = e.clientY+"px"; + //dragdata.indicator.style.left = e.clientX+"px"; + + if (dragdata.indicator) { + var storeIndicatorTopPos = dragdata.indicator.style.top; + //console.log("INDICATOR BEFORE: "+dragdata.indicator.style.top+" "+dragdata.indicator.style.left); + //get Element at x, y + dragdata.indicator.style.display = "block"; + dragdata.indicator.style.top = "10000px"; + } + apf.DragServer.dragdata.x = e.pageX ? e.pageX - (!apf.isIE + ? window.pageXOffset + : 0) : c.clientX; + apf.DragServer.dragdata.y = e.pageY ? e.pageY - (!apf.isIE + ? window.pageYOffset + : 0) : c.clientY; + var el = document.elementFromPoint(apf.DragServer.dragdata.x, + apf.DragServer.dragdata.y); + if (!el) { + el = document.elementFromPoint(apf.DragServer.dragdata.x, + apf.DragServer.dragdata.y); + } + + if (dragdata.indicator) + dragdata.indicator.style.top = storeIndicatorTopPos; + //console.log("INDICATOR AFTER: "+dragdata.indicator.style.top+" " + //+dragdata.indicator.style.left+" "+apf.DragServer.dragdata.x+" "+apf.DragServer.dragdata.y); + //Set Indicator + dragdata.host.$moveDragIndicator(c); + + //get element and call events + var receiver = apf.findHost(el); + + //Run Events + if (receiver) + apf.DragServer.dragover(receiver, el, e); + else if (apf.DragServer.last) + apf.DragServer.dragout(apf.DragServer.last, e); + + apf.DragServer.lastTime = new Date().getTime(); + }, + + onmouseup : function(e){ + e = e || window.event; + + + var c = { + clientX: e.pageX ? e.pageX - window.pageXOffset : e.clientX, + clientY: e.pageY ? e.pageY - window.pageYOffset : e.clientY + }; + + if (!apf.DragServer.dragdata.started + && Math.abs(apf.DragServer.coordinates.clientX - c.clientX) < 6 + && Math.abs(apf.DragServer.coordinates.clientY - c.clientY) < 6) { + apf.DragServer.stop(true, null, e) + return; + } + + //get Element at x, y + var indicator = apf.DragServer.dragdata.indicator, + storeIndicatorTopPos = indicator.style.top; + //apf.console.info("INDICATOR UP BEFORE: "+indicator.style.top+" "+indicator.style.left); + if (indicator) + indicator.style.top = "10000px"; + + apf.DragServer.dragdata.x = e.pageX ? e.pageX - (!apf.isIE + ? window.pageXOffset + : 0) : c.clientX; + apf.DragServer.dragdata.y = e.pageY ? e.pageY - (!apf.isIE + ? window.pageYOffset + : 0) : c.clientY; + + var el = document.elementFromPoint(apf.DragServer.dragdata.x, + apf.DragServer.dragdata.y); + if (!el) { + el = document.elementFromPoint(apf.DragServer.dragdata.x, + apf.DragServer.dragdata.y); + } + + indicator.style.top = storeIndicatorTopPos; + //apf.console.info("INDICATOR UP AFTER: "+indicator.style.top+" "+indicator.style.left); + + //get element and call events + var host = apf.findHost(el); + + //Run Events + if (apf.DragServer.host && host != apf.DragServer.host) + apf.DragServer.dragout(apf.DragServer.host, e); + var success = apf.DragServer.dragdrop(host, el, apf.DragServer.dragdata.host, e); + apf.DragServer.stop(true, success, e); + } +}; + +/** + * @private + */ +apf.MultiselectDragDrop = function() { + /**** Drag & Drop ****/ + + this.diffX = + this.diffY = 0; + this.multiple = false; + this.lastDragNode = null; + this.lastel = null; + + this.$showDragIndicator = function(sel, e){ + var srcEl = e.originalTarget || e.srcElement || e.target; + + this.multiple = sel.length > 1; + + if (this.multiple) { + this.diffX = e.scrollX; + this.diffY = e.scrollY; + } + else { + var itemNode = apf.xmldb.findHtmlNode(sel[0], this); + this.diffX = -1 * (e.offsetX - parseInt(apf.getStyleRecur(itemNode, "padding-left").replace(/px$/, "") - 10)); + this.diffY = -1 * e.offsetY; + } + + var prefix = this.oDrag.className.split(" ")[0] + //@todo the class should be removed here + this.$setStyleClass(this.oDrag, (this.multiple + ? prefix + "_multiple" : "") + (this["class"] ? " " + this["class"] : ""), [prefix + "_multiple"]); + + if (this.multiple) { + document.body.appendChild(this.oDrag); + return this.oDrag; + } + else if (this.localName == "datagrid") { + if (this.lastDragNode) + apf.destroyHtmlNode(this.lastDragNode); + + sel = this.$selected || this.$caret; + var oDrag = sel.cloneNode(true); + oDrag.removeAttribute("onmousedown");oDrag.onmousedown = null; + oDrag.removeAttribute("onmouseup");oDrag.onmouseup = null; + oDrag.removeAttribute("onmouseout");oDrag.onmouseout = null; + oDrag.removeAttribute("ondblclick");oDrag.ondblclick = null; + document.body.appendChild(oDrag); + + oDrag.style.position = "absolute"; + oDrag.style.width = sel.offsetWidth + "px"; + oDrag.style.display = "none"; + oDrag.removeAttribute("id"); + + this.$setStyleClass(oDrag, "draggrid"); + var nodes = sel.childNodes; + var dragnodes = oDrag.childNodes; + for (var i = nodes.length - 1; i >= 0; i--) { + if (dragnodes[i].nodeType == 1) + dragnodes[i].style.width = apf.getStyle(nodes[i], "width"); + } + //@todo apf3.0 remove all the event handlers of the children. + return (this.lastDragNode = oDrag); + } + else { + var sel = this.$selected || this.$caret, + width = apf.getStyle(this.oDrag, "width"); + + if (!sel) + return; + + if (!width || width == "auto") + this.oDrag.style.width = (sel.offsetWidth - apf.getWidthDiff(this.oDrag)) + "px"; + this.$updateNode(this.selected, this.oDrag); + } + + apf.window.zManager.set("drag", this.oDrag); + + return this.oDrag; + }; + + this.$hideDragIndicator = function(success){ + var oDrag = this.lastDragNode || this.oDrag, _self = this; + if (!this.multiple && !success && oDrag.style.display == "block") { + if (!this.$selected && !this.$caret) + return; + + var pos = apf.getAbsolutePosition(this.$selected || this.$caret); + apf.tween.multi(oDrag, { + anim : apf.tween.easeInOutCubic, + steps : apf.isIE ? 15 : 20, + interval : 15, + tweens : [ + {type: "left", from: oDrag.offsetLeft, to: (pos[0] + parseInt(apf.getStyleRecur(this.$selected, "padding-left").replace(/px$/, "")))}, + {type: "top", from: oDrag.offsetTop, to: pos[1]} + ], + onfinish : function(){ + if (_self.lastDragNode) { + apf.destroyHtmlNode(_self.lastDragNode); + _self.lastDragNode = null; + } + else { + _self.oDrag.style.display = "none"; + } + } + }); + } + else if (this.lastDragNode) { + apf.destroyHtmlNode(this.lastDragNode); + this.lastDragNode = null; + } + else { + this.oDrag.style.display = "none"; + } + }; + + this.$moveDragIndicator = function(e){ + var oDrag = this.lastDragNode || this.oDrag; + oDrag.style.left = (e.clientX + this.diffX) + "px";// - this.oDrag.startX + oDrag.style.top = (e.clientY + this.diffY + (this.multiple ? 15 : 0)) + "px";// - this.oDrag.startY + }; + + this.addEventListener("$skinchange", function(){ + this.$initDragDrop(); + }); + + this.$initDragDrop = function(){ + if (!this.$hasLayoutNode("dragindicator")) + return; + + this.oDrag = apf.insertHtmlNode( + this.$getLayoutNode("dragindicator"), document.body); + + apf.window.zManager.set("drag", this.oDrag); + + this.oDrag.style.position = "absolute"; + this.oDrag.style.cursor = "default"; + this.oDrag.style.display = "none"; + }; + + this.$findValueNode = function(el){ + if (!el) return null; + + while(el && el.nodeType == 1 + && !el.getAttribute(apf.xmldb.htmlIdTag)) { + if (this.$isTreeArch && el.previousSibling + && el.previousSibling.nodeType == 1) //@todo hack!! apf3.0 fix this. + el = el.previousSibling; + else + el = el.parentNode; + } + + return (el && el.nodeType == 1 && el.getAttribute(apf.xmldb.htmlIdTag)) + ? el + : null; + }; + + + this.$dragout = function(el, dragdata, extra){ + if (this.lastel) + this.$setStyleClass(this.lastel, "", ["dragDenied", "dragInsert", + "dragAppend", "selected", "indicate"]); + + var sel = this.$getSelection(true); + for (var i = 0, l = sel.length; i < l; i++) + this.$setStyleClass(sel[i], "selected", ["dragDenied", + "dragInsert", "dragAppend", "indicate"]); + + this.$setStyleClass(this.$ext, "", [this.$baseCSSname + "Drop"]); + + this.lastel = null; + }; + + if (!this.$dragdrop) + this.$dragdrop = this.$dragout; + + this.$dragover = function(el, dragdata, extra){ + this.$setStyleClass(this.$ext, this.$baseCSSname + "Drop"); + + var sel = this.$getSelection(true); + for (var i = 0, l = sel.length; i < l; i++) + this.$setStyleClass(sel[i], "", ["dragDenied", + "dragInsert", "dragAppend", "selected", "indicate"]); + + if (this.lastel) + this.$setStyleClass(this.lastel, "", ["dragDenied", + "dragInsert", "dragAppend", "selected", "indicate"]); + + var action = extra[1] && extra[1].action; + this.lastel = this.$findValueNode(el); + if (this.$isTreeArch && action == "list-append") { + var htmlNode = apf.xmldb.findHtmlNode(this.getTraverseParent(apf.xmldb.getNode(this.lastel)), this); + + this.lastel = htmlNode + ? this.$getLayoutNode("item", "container", htmlNode) + : this.$container; + + this.$setStyleClass(this.lastel, "dragInsert"); + } + else { + this.$setStyleClass(this.lastel, extra + ? (action == "insert-before" + ? "dragInsert" + : "dragAppend") + : "dragDenied"); + } + }; + +}; + +/** + * @private + */ +apf.StandardDragDrop = function() { + this.$showDragIndicator = function(sel, e){ + var x = e.offsetX + 22, + y = e.offsetY; + + this.oDrag.startX = x; + this.oDrag.startY = y; + + + document.body.appendChild(this.oDrag); + //this.oDrag.getElementsByTagName("DIV")[0].innerHTML = this.selected.innerHTML; + //this.oDrag.getElementsByTagName("IMG")[0].src = this.selected.parentNode.parentNode.childNodes[1].firstChild.src; + var oInt = this.$getLayoutNode("main", "caption", this.oDrag); + if (oInt.nodeType != 1) + oInt = oInt.parentNode; + + oInt.innerHTML = this.$applyBindRule("caption", this.xmlRoot) || ""; + + return this.oDrag; + }; + + this.$hideDragIndicator = function(){ + this.oDrag.style.display = "none"; + }; + + this.$moveDragIndicator = function(e){ + this.oDrag.style.left = (e.clientX - this.oDrag.startX + + document.documentElement.scrollLeft) + "px"; + this.oDrag.style.top = (e.clientY - this.oDrag.startY + + document.documentElement.scrollTop) + "px"; + }; + + //@todo falsely assuming only attributes are used for non multiselect widgets + this.$initDragDrop = function(){ + if (!this.getAttribute("drag")) + return; + + this.oDrag = document.body.appendChild(this.$ext.cloneNode(true)); + + apf.window.zManager.set("drag", this.oDrag); + + this.oDrag.style.position = "absolute"; + this.oDrag.style.cursor = "default"; + this.oDrag.style.filter = "progid:DXImageTransform.Microsoft.Alpha(opacity=50)"; + this.oDrag.style.MozOpacity = 0.5; + this.oDrag.style.opacity = 0.5; + this.oDrag.style.display = "none"; + }; +}; + +apf.DragServer.Init(); + + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/core/baseclasses/focussable.js)SIZE(3405)TIME(Fri, 14 Oct 2011 08:01:44 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + +apf.__FOCUSSABLE__ = 1 << 26; + + +apf.Focussable = function(){ + this.$regbase = this.$regbase | apf.__FOCUSSABLE__; + if (this.disabled == undefined) + this.disabled = false; + + /** + * Sets the position in the list that determines the sequence + * of elements when using the tab key to move between them. + * Call-chaining is supported. + * @param {Number} tabindex the position in the list + */ + this.setTabIndex = function(tabindex){ + apf.window.$removeFocus(this); + apf.window.$addFocus(this, tabindex); + return this; + }; + + /** + * Gives this element the focus. This means that keyboard events + * are send to this element. + */ + this.focus = function(noset, e, nofix){ + if (!noset) { + if (this.$isWindowContainer > -1) { + apf.window.$focusLast(this, e, true); + } + else { + apf.window.$focus(this, e); + + + if (!nofix && apf.hasFocusBug) + apf.window.$focusfix(); + + } + + return this; + } + + if (this.$focus && !this.editable && (!e || !e.mouse || this.$focussable == apf.KEYBOARD_MOUSE)) + this.$focus(e); + + this.dispatchEvent("focus", apf.extend({ + bubbles : true + }, e)); + return this; + }; + + /** + * Removes the focus from this element. + * Call-chaining is supported. + */ + this.blur = function(noset, e){ + + if ((e && !apf.isChildOf(e.fromElement, e.toElement)) && apf.popup.isShowing(this.$uniqueId)) + apf.popup.forceHide(); //This should be put in a more general position + + + if (this.$blur) + this.$blur(e); + + if (!noset) + apf.window.$blur(this); + + this.dispatchEvent("blur", apf.extend({ + bubbles : !e || !e.cancelBubble + }, e)); + return this; + }; + + /** + * Determines whether this element has the focus + * @returns {Boolean} indicating whether this element has the focus + */ + this.hasFocus = function(){ + return apf.document.activeElement == this || this.$isWindowContainer + && (apf.document.activeElement || {}).$focusParent == this; + }; +}; + + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/core/baseclasses/interactive.js)SIZE(30523)TIME(Mon, 19 Dec 2011 11:09:29 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + +apf.__INTERACTIVE__ = 1 << 21; + + + +/** + * All elements inheriting from this {@link term.baseclass baseclass} have interactive features, making an + * element draggable and resizable. + * Example: + * + * + * + * + * @attribute {Boolean} draggable whether an element is draggable. The user will + * able to move the element around while holding the mouse button down on the + * element. + * Example: + * + * + * + * @attribute {Boolean} resizable whether an element is resizable. The user will able + * to resize the element by grabbing one of the four edges of the element and + * pulling it in either direction. Grabbing the corners allows users to + * resize horizontally and vertically at the same time. The right bottom corner + * is special, because it offers an especially big grab area. The size of this + * area can be configured in the skin of the element. + * Example: + * + * + * + * @attribute {Number} minwidth the minimum horizontal size the element can get when resizing. + * @attribute {Number} minheight the minimum vertical size the element can get when resizing. + * @attribute {Number} maxwidth the maximum horizontal size the element can get when resizing. + * @attribute {Number} maxheight the maximum vertical size the element can get when resizing. + * + * @event drag Fires when the widget has been dragged. + * @event resizestart Fires before the widget is resized. + * cancelable: Prevents this resize action to start. + * object: + * {String} type the type of resize. This is a combination of the four directions, n, s, e, w. + * @event resize Fires when the widget has been resized. + * + * @constructor + * + * @author Ruben Daniels (ruben AT ajax DOT org) + * @version %I%, %G% + * @since 1.0 + * + * @see element.appsettings.attribute.outline + * @see element.appsettings.attribute.resize-outline + * @see element.appsettings.attribute.drag-outline + */ +apf.Interactive = function(){ + var nX, nY, rX, rY, startPos, lastCursor = null, l, t, r, b, lMax, tMax, lMin, + tMin, w, h, we, no, ea, so, rszborder, rszcorner, marginBox, + verdiff, hordiff, _self = this, posAbs, oX, oY, overThreshold, + dragOutline, resizeOutline, myPos, startGeo; + + this.$regbase = this.$regbase | apf.__INTERACTIVE__; + + this.$dragStart = function(e, reparent){ + var nativeEvent = e || event; + if (!reparent && nativeEvent.button == 2) + return; + + + { + dragStart.apply(nativeEvent.srcElement || this, arguments); + } + } + + this.$propHandlers["draggable"] = function(value){ + if (apf.isFalse(value)) + this.draggable = value = false; + else if (apf.isTrue(value)) + this.draggable = value = true; + + var o = this.editable ? this.$ext : this.oDrag || this.$ext; + if (value) + apf.addListener(o, "mousedown", this.$dragStart); + else + apf.removeListener(o, "mousedown", this.$dragStart); + + //deprecated?? + if (o.interactive & 1) + return; + o.interactive = (o.interactive||0)+1; + + //this.$ext.style.position = "absolute"; + }; + + this.$propHandlers["resizable"] = function(value){ + if (apf.isFalse(value)) + this.resizable = false; + else if (apf.isTrue(value)) + this.resizable = "true"; + + this.$ext.style.cursor = ""; + + var o = this.oResize || this.$ext; + if (o.interactive & 2) + return; + + if (!_self.editable) { + apf.addListener(o, "mousedown", function(){ + resizeStart.apply(o, arguments); + }); + + apf.addListener(o, "mousemove", function(){ + resizeIndicate.apply(o, arguments); + }); + } + + o.interactive = (o.interactive||0)+2; + + //this.$ext.style.position = "absolute"; + + rszborder = this.$getOption && parseInt(this.$getOption("Main", "resize-border")) || 3; + rszcorner = this.$getOption && parseInt(this.$getOption("Main", "resize-corner")) || 12; + marginBox = apf.getBox(apf.getStyle(this.$ext, "borderWidth")); + }; + + /* + this.$propHandlers["minwidth"] = + this.$propHandlers["maxwidth"] = + this.$propHandlers["minheight"] = + this.$propHandlers["maxheight"] = function(value, prop){ + if (this.aData) + this.aData[prop] = parseInt(value); + } + if (this.aData) { + this.aData.minwidth = this.minwidth; + this.aData.minheight = this.minheight; + }*/ + + this.$cancelInteractive = function(){ + document.onmouseup(null, true); + } + + function dragStart(e, reparent){ + if (!e) e = event; + + if (!reparent && (!_self.draggable || apf.dragMode))//_self.editable || + return; + + + dragOutline = _self.dragOutline == true || apf.config.dragOutline; + + + if (_self.dispatchEvent("beforedragstart", {htmlEvent: e}) === false) + return; + + apf.dragMode = true; + if (reparent) { + _self.dispatchEvent("beforedrag") + overThreshold = true; + } + else + overThreshold = false; + + + apf.popup.forceHide(); + + + posAbs = "absolute|fixed".indexOf(apf.getStyle(_self.$ext, "position")) > -1; + if (!posAbs) { + _self.$ext.style.position = posAbs //(posAbs = _self.dragSelection) + ? "absolute" : "relative"; + } + if (_self.editable) + posAbs = true; + + //@todo not for docking + + if (posAbs && !_self.aData) { + apf.plane.show(dragOutline + ? oOutline + : _self.$ext, e.reappend);//, true + } + + + var ext = (reparent || (oOutline && oOutline.self)) && dragOutline //little dirty hack to detect outline set by visualselect + ? oOutline + : _self.$ext; + var pos = posAbs + ? apf.getAbsolutePosition(ext, ext.offsetParent, true) + : [parseInt(apf.getStyle(ext, "left")) || 0, + parseInt(apf.getStyle(ext, "top")) || 0]; + + + startGeo = [ext.style.left, ext.style.top, ext.style.right, + ext.style.bottom, ext.style.width, ext.style.height]; + + + nX = pos[0] - (oX = e.clientX); + nY = pos[1] - (oY = e.clientY); + + //if (_self.hasFeature && _self.hasFeature(apf.__ANCHORING__)) + //_self.$disableAnchoring(); + + if (!(reparent || (oOutline && oOutline.self))) { + + if (posAbs && dragOutline) { + oOutline.className = "drag"; + + var diffOutline = apf.getDiff(oOutline); + _self.$ext.offsetParent.appendChild(oOutline); + oOutline.style.left = pos[0] + "px"; + oOutline.style.top = pos[1] + "px"; + oOutline.style.width = (_self.$ext.offsetWidth - diffOutline[0]) + "px"; + oOutline.style.height = (_self.$ext.offsetHeight - diffOutline[1]) + "px"; + + if (_self.editable) + oOutline.style.display = "block"; + } + else + + { + if (_self.$ext.style.right) { + _self.$ext.style.left = pos[0] + "px"; + _self.$ext.style.right = ""; + } + if (_self.$ext.style.bottom) { + _self.$ext.style.top = pos[1] + "px"; + _self.$ext.style.bottom = ""; + } + } + } + + document.onmousemove = dragMove; + document.onmouseup = function(e, cancel){ + document.onmousemove = document.onmouseup = null; + + + if (posAbs && !_self.aData) + apf.plane.hide(); + + + var htmlNode = dragOutline + ? oOutline + : _self.$ext; + + if (overThreshold && !_self.$multidrag) { + + if (cancel) { + var ext = _self.$ext; + ext.style.left = startGeo[0]; + ext.style.top = startGeo[1]; + ext.style.right = startGeo[2]; + ext.style.bottom = startGeo[3]; + ext.style.width = startGeo[4]; + ext.style.height = startGeo[5]; + + if (_self.dispatchEvent) + _self.dispatchEvent("dragcancel", { + htmlNode : htmlNode, + htmlEvent : e + }); + } + else + + + if (_self.setProperty) { + updateProperties(); + } + else if (dragOutline) { + _self.$ext.style.left = l + "px"; + _self.$ext.style.top = t + "px"; + } + } + + l = t = w = h = null; + + if (!posAbs) + _self.$ext.style.position = "relative"; + + if (_self.showdragging) + apf.setStyleClass(_self.$ext, "", ["dragging"]); + + if (posAbs && dragOutline && !oOutline.self) //little dirty hack to detect outline set by visualselect + oOutline.style.display = "none"; + + apf.dragMode = false; + + if (!cancel && _self.dispatchEvent && overThreshold) + _self.dispatchEvent("afterdrag", { + htmlNode : htmlNode, + htmlEvent : e + }); + }; + + if (reparent) + document.onmousemove(e); + //else if (apf.isIE) + //apf.window.$mousedown(e); + + return false; + }; + + function dragMove(e){ + if(!e) e = event; + + //if (_self.dragSelection) + //overThreshold = true; + + if (!overThreshold && _self.showdragging) + apf.setStyleClass(_self.$ext, "dragging"); + + // usability rule: start dragging ONLY when mouse pointer has moved delta x pixels + var dx = e.clientX - oX, + dy = e.clientY - oY, + distance; + + if (!overThreshold + && (distance = dx*dx > dy*dy ? dx : dy) * distance < 2) + return; + + //Drag outline support + else if (!overThreshold) { + if (dragOutline + && oOutline.style.display != "block") + oOutline.style.display = "block"; + + if (_self.dispatchEvent && _self.dispatchEvent("beforedrag", {htmlEvent: e}) === false) { + document.onmouseup(); + return; + } + } + + var oHtml = dragOutline + ? oOutline + : _self.$ext; + + oHtml.style.left = (l = e.clientX + nX) + "px"; + oHtml.style.top = (t = e.clientY + nY) + "px"; + + if (_self.realtime) { + var change = _self.$stick = {}; + _self.$showDrag(l, t, oHtml, e, change); + + if (typeof change.l != "undefined") + l = change.l, oHtml.style.left = l + "px"; + if (typeof change.t != "undefined") + t = change.t, oHtml.style.top = t + "px"; + } + + overThreshold = true; + }; + + this.$resizeStart = resizeStart; + function resizeStart(e, options){ + if (!e) e = event; + + //|| _self.editable + if (!_self.resizable + || String(_self.height).indexOf("%") > -1 && _self.parentNode.localName == "vbox" //can't resize percentage based for now + || String(_self.width).indexOf("%") > -1 && _self.parentNode.localName == "hbox") //can't resize percentage based for now + return; + + + resizeOutline = !(_self.resizeOutline == false || !apf.config.resizeOutline); + + + var ext = _self.$ext; + if (!resizeOutline) { + var diff = apf.getDiff(ext); + hordiff = diff[0]; + verdiff = diff[1]; + } + + //@todo This is probably not gen purpose + startPos = apf.getAbsolutePosition(ext);//, ext.offsetParent); + startPos.push(ext.offsetWidth); + startPos.push(ext.offsetHeight); + myPos = apf.getAbsolutePosition(ext, ext.offsetParent, true); + + + startGeo = [ext.style.left, ext.style.top, ext.style.right, + ext.style.bottom, ext.style.width, ext.style.height]; + + + var sLeft = 0, + sTop = 0, + x = (oX = e.clientX) - startPos[0] + sLeft + document.documentElement.scrollLeft, + y = (oY = e.clientY) - startPos[1] + sTop + document.documentElement.scrollTop, + resizeType; + + if (options && options.resizeType) { + posAbs = "absolute|fixed".indexOf(apf.getStyle(ext, "position")) > -1; + resizeType = options.resizeType; + } + else { + resizeType = getResizeType.call(ext, x, y); + } + rX = x; + rY = y; + + if (!resizeType) + return; + + if (_self.dispatchEvent && _self.dispatchEvent("beforeresize", { + type : resizeType, + setType : function(type){ + resizeType = type; + } + }) === false) { + //if (apf.isIE) + //apf.window.$mousedown(e); //@todo is this necessary? + return; + } + + + apf.popup.forceHide(); + + + //if (_self.hasFeature && _self.hasFeature(apf.__ANCHORING__)) + //_self.$disableAnchoring(); + + apf.dragMode = true; + overThreshold = false; + + we = resizeType.indexOf("w") > -1; + no = resizeType.indexOf("n") > -1; + ea = resizeType.indexOf("e") > -1; + so = resizeType.indexOf("s") > -1; + + if (!_self.minwidth) _self.minwidth = 0; + if (!_self.minheight) _self.minheight = 0; + if (!_self.maxwidth) _self.maxwidth = 10000; + if (!_self.maxheight) _self.maxheight = 10000; + + if (posAbs) { + lMax = myPos[0] + startPos[2]; + tMax = myPos[1] + startPos[3]; + lMin = myPos[0] + startPos[2]; + tMin = myPos[1] + startPos[3]; + } + + + if (posAbs) { + apf.plane.show(resizeOutline + ? oOutline + : ext);//, true + } + + + var iMarginLeft; + + + if (resizeOutline) { + oOutline.className = "resize"; + var diffOutline = apf.getDiff(oOutline); + hordiff = diffOutline[0]; + verdiff = diffOutline[1]; + + //ext.parentNode.appendChild(oOutline); + oOutline.style.left = startPos[0] + "px"; + oOutline.style.top = startPos[1] + "px"; + oOutline.style.width = (ext.offsetWidth - hordiff) + "px"; + oOutline.style.height = (ext.offsetHeight - verdiff) + "px"; + oOutline.style.display = "block"; + } + else + + { + if (ext.style.right) { + ext.style.left = myPos[0] + "px"; + + //console.log(myPos[0]); + //ext.style.right = ""; + } + if (ext.style.bottom) { + ext.style.top = myPos[1] + "px"; + //ext.style.bottom = ""; + } + } + + if (!options || !options.nocursor) { + if (lastCursor === null) + lastCursor = document.body.style.cursor;//apf.getStyle(document.body, "cursor"); + document.body.style.cursor = getCssCursor(resizeType) + "-resize"; + } + + document.onmousemove = resizeMove; + document.onmouseup = function(e, cancel){ + document.onmousemove = document.onmouseup = null; + + + if (posAbs) + apf.plane.hide(); + + + clearTimeout(timer); + + if (resizeOutline) { + var diff = apf.getDiff(_self.$ext); + hordiff = diff[0]; + verdiff = diff[1]; + } + + + if (cancel) { + var ext = _self.$ext; + ext.style.left = startGeo[0]; + ext.style.top = startGeo[1]; + ext.style.right = startGeo[2]; + ext.style.bottom = startGeo[3]; + ext.style.width = startGeo[4]; + ext.style.height = startGeo[5]; + + if (_self.dispatchEvent) + _self.dispatchEvent("resizecancel"); + } + else + + doResize(e || event, true); + + if (_self.setProperty) + updateProperties(); + + document.body.style.cursor = lastCursor || ""; + lastCursor = null; + + if (resizeOutline) + oOutline.style.display = "none"; + + apf.dragMode = false; + + if (!cancel && _self.dispatchEvent) + _self.dispatchEvent("afterresize", { + l: l, t: t, w: w+hordiff, h: h+verdiff + }); + + l = t = w = h = null; + }; + + //if (apf.isIE) + //apf.window.$mousedown(e); + + return false; + } + + function updateProperties(left, top, width, height, hdiff, vdiff, right, bottom){ + if (typeof left == "undefined") { + left = l, top = t, width = w, height = h, + vdiff = verdiff, hdiff = hordiff; + } + else posAbs = true; + + var hasLeft = _self.left || _self.left === 0; + var hasRight = _self.right || _self.right === 0; + var hasBottom = _self.bottom || _self.bottom === 0; + var hasTop = _self.top || _self.top === 0; + + if (posAbs) { + var htmlNode = (oOutline && oOutline.style.display == "block") + ? oOutline + : _self.$ext; + + if (hasRight && !(right || right === 0)) + right = apf.getHtmlRight(htmlNode); + + if (hasBottom && !(bottom || bottom === 0)) + bottom = apf.getHtmlBottom(htmlNode); + + if (hasRight) { + _self.setProperty("right", right, 0, _self.editable); + if (!_self.left) + htmlNode.style.left = ""; + } + + if (hasBottom) { + _self.setProperty("bottom", bottom, 0, _self.editable); + if (!_self.top) + htmlNode.style.top = ""; + } + + if ((left || left === 0) && (!hasRight || hasLeft)) + _self.setProperty("left", left, 0, _self.editable); + if ((top || top === 0) && (!hasBottom || hasTop)) + _self.setProperty("top", top, 0, _self.editable); + } + + if (hdiff != undefined && width && (!hasLeft || !hasRight)) + _self.setProperty("width", width + hdiff, 0, _self.editable) + if (vdiff != undefined && height && (!hasTop || !hasBottom)) + _self.setProperty("height", height + vdiff, 0, _self.editable); + } + this.$updateProperties = updateProperties; + + var min = Math.min, max = Math.max, lastTime, timer; + function resizeMove(e){ + if(!e) e = event; + + //if (!e.button) + //return this.onmouseup(); + + // usability rule: start dragging ONLY when mouse pointer has moved delta x pixels + /*var dx = e.clientX - oX, + dy = e.clientY - oY, + distance; + + if (!overThreshold + && (distance = dx*dx > dy*dy ? dx : dy) * distance < 4) + return;*/ + + clearTimeout(timer); + if (lastTime && new Date().getTime() + - lastTime < (resizeOutline ? 6 : apf.mouseEventBuffer)) { + var z = { + clientX: e.clientX, + clientY: e.clientY + } + timer = $setTimeout(function(){ + doResize(z); + }, 10); + return; + } + lastTime = new Date().getTime(); + + doResize(e); + + //overThreshold = true; + } + + function doResize(e, force){ + var oHtml = resizeOutline && !force + ? oOutline + : _self.$ext; + + var sLeft = document.documentElement.scrollLeft, + sTop = document.documentElement.scrollTop; + + if (we) { + if (posAbs) + oHtml.style.left = (l = max((lMin - _self.maxwidth), + min((lMax - _self.minwidth), + myPos[0] + e.clientX - oX + sLeft))) + "px"; + oHtml.style.width = (w = min(_self.maxwidth - hordiff, + max(hordiff, _self.minwidth, + startPos[2] - (e.clientX - oX) + sLeft + ) - hordiff)) + "px"; //@todo + } + + if (no) { + if (posAbs) + oHtml.style.top = (t = max((tMin - _self.maxheight), + min((tMax - _self.minheight), + myPos[1] + e.clientY - oY + sTop))) + "px"; + oHtml.style.height = (h = min(_self.maxheight - verdiff, + max(verdiff, _self.minheight, + startPos[3] - (e.clientY - oY) + sTop + ) - verdiff)) + "px"; //@todo + } + + if (ea) + oHtml.style.width = (w = min(_self.maxwidth - hordiff, + max(hordiff, _self.minwidth, + e.clientX - startPos[0] + (startPos[2] - rX) + sLeft) + - hordiff)) + "px"; + + if (so) + oHtml.style.height = (h = min(_self.maxheight - verdiff, + max(verdiff, _self.minheight, + e.clientY - startPos[1] + (startPos[3] - rY) + sTop) + - verdiff)) + "px"; + + //@todo apf3.0 this is execution wise inefficient + if (_self.parentNode && _self.parentNode.localName == "table") { + updateProperties(); + apf.layout.processQueue(); + } + + if (_self.realtime) { + var change = _self.$stick = {}; + + //@todo calc l and t once at start of resize (subtract borders) + _self.$showResize(l || apf.getHtmlLeft(oHtml), t || apf.getHtmlTop(oHtml), + w && w + hordiff || oHtml.offsetWidth, + h && h + verdiff || oHtml.offsetHeight, e, change, we, no, ea, so); + + if (posAbs && we && typeof change.l != "undefined") + oHtml.style.left = (l = max((lMin - _self.maxwidth), min((lMax - _self.minwidth), change.l))) + "px"; + + if (posAbs && no && typeof change.t != "undefined") + oHtml.style.top = (t = max((tMin - _self.maxheight), min((tMax - _self.minheight), change.t))) + "px"; + + if (typeof change.w != "undefined") + oHtml.style.width = (w = min(_self.maxwidth - hordiff, + max(hordiff, _self.minwidth, + change.w) - hordiff)) + "px"; + if (typeof change.h != "undefined") + oHtml.style.height = (h = min(_self.maxheight - verdiff, + max(verdiff, _self.minheight, + change.h) - verdiff)) + "px"; + } + + + if (apf.hasSingleRszEvent) + apf.layout.forceResize(_self.$int); + + } + + function getResizeType(x, y){ + var cursor = "", + tcursor = ""; + posAbs = "absolute|fixed".indexOf(apf.getStyle(_self.$ext, "position")) > -1; + if (_self.resizable == "true" || _self.resizable == "vertical" || _self.resizable.indexOf('top') > -1 || _self.resizable.indexOf('bottom') > -1) { + if (y < rszborder + marginBox[0] && _self.resizable.indexOf('bottom') == -1) + cursor = posAbs ? "n" : ""; + else if (y > this.offsetHeight - (rszcorner || rszborder) && _self.resizable.indexOf('top') == -1) //marginBox[0] - marginBox[2] - + cursor = "s"; + } + + if (_self.resizable == "true" || _self.resizable == "horizontal" || _self.resizable.indexOf('left') > -1 || _self.resizable.indexOf('right') > -1) { + if (x < (cursor ? rszcorner : rszborder) + marginBox[0] && _self.resizable.indexOf('right') == -1) + cursor += tcursor + (posAbs ? "w" : ""); + else if (x > this.offsetWidth - (cursor || tcursor ? rszcorner : rszborder) && _self.resizable.indexOf('left') == -1) //marginBox[1] - marginBox[3] - + cursor += tcursor + "e"; + } + + return cursor; + } + + var originalCursor; + function resizeIndicate(e){ + if(!e) e = event; + + if (!_self.resizable || _self.editable || document.onmousemove) + return; + + //@todo This is probably not gen purpose + var pos = apf.getAbsolutePosition(_self.$ext),//, _self.$ext.offsetParent + sLeft = 0, + sTop = 0, + x = e.clientX - pos[0] + sLeft + document.documentElement.scrollLeft, + y = e.clientY - pos[1] + sTop + document.documentElement.scrollTop; + + if (!originalCursor) + originalCursor = apf.getStyle(this, "cursor"); + + var cursor = getResizeType.call(_self.$ext, x, y); + + this.style.cursor = cursor + ? getCssCursor(cursor) + "-resize" + : originalCursor || "default"; + }; + + function getCssCursor(cursor){ + var cssCursor = cursor; + if (apf.isWebkit) { + if (cursor == "se" || cursor == "nw") + cssCursor = "nwse"; + else if (cursor == "sw" || cursor == "ne") + cssCursor = "nesw"; + else if (cursor == "s" || cursor == "n") + cssCursor = "ns"; + else if (cursor == "e" || cursor == "w") + cssCursor = "ew"; + } + + return cssCursor; + } + + var oOutline; + + function initOutline(e){ + var doc = this.$pHtmlDoc || document; + oOutline = doc.getElementById("apf_outline"); + if (!oOutline) { + oOutline = doc.body.appendChild(doc.createElement("div")); + + oOutline.refCount = 0; + oOutline.setAttribute("id", "apf_outline"); + + oOutline.style.position = "absolute"; + oOutline.style.display = "none"; + //oOutline.style.zIndex = 2000000; + apf.window.zManager.set("drag", oOutline); + oOutline.host = false; + } + oOutline.refCount++ + } + + if (this.addEventListener && this.hasFeature(apf.__AMLNODE__) && !this.$amlLoaded) { + if (document.body) + initOutline.call(this); + else + this.addEventListener("DOMNodeInsertedIntoDocument", initOutline); + } + else { + this.$pHtmlDoc = document; + initOutline.call(this); + } + + this.$setOutline = function(o){ + oOutline = o; + } + + + /*this.addEventListener("DOMNodeRemovedFromDocument", function(e){ + oOutline.refCount--; + + if (!oOutline.refCount) { + //destroy + } + });*/ +}; + +apf.GuiElement.propHandlers["resizable"] = function(value){ + this.implement(apf.Interactive); + this.$propHandlers["resizable"].apply(this, arguments); +} + +apf.GuiElement.propHandlers["draggable"] = function(value){ + this.implement(apf.Interactive); + this.$propHandlers["draggable"].apply(this, arguments); +}; + +apf.Init.run("interactive"); + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/core/baseclasses/media.js)SIZE(18898)TIME(Fri, 14 Oct 2011 08:01:44 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + +apf.__MEDIA__ = 1 << 20; + + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/core/baseclasses/multicheck.js)SIZE(16594)TIME(Fri, 14 Oct 2011 08:01:44 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + +apf.__MULTICHECK__ = 1 << 22; + + + +/** + * All elements inheriting from this {@link term.baseclass baseclass} have checkable items. + * + * @constructor + * @baseclass + * @author Ruben Daniels (ruben AT ajax DOT org) + * @version %I%, %G% + * @since 3.0 + * + * @todo type detection, errors (see functions in multiselect) + */ +apf.MultiCheck = function(){ + this.$regbase = this.$regbase | apf.__MULTICHECK__; + + /**** Properties ****/ + + this.multicheck = true; + this.checklength = 0; + this.$checkedList = []; + + /**** Public Methods ****/ + + /** + * Checks a single, or set of. + * The checking can be visually represented in this element. + * The element can be checked, partialy checked or unchecked + * + * @param {mixed} xmlNode the identifier to determine the selection. + * @return {Boolean} whether the selection could not be made + * + * @event beforecheck Fires before a check is made + * + * @event aftercheck Fires after a check is made + * + */ + this.check = function(xmlNode, userAction){ + if (userAction && this.disabled + || this.$checkedList.indexOf(xmlNode) > -1) + return; + + if (userAction + && this.$executeSingleValue("check", "checked", xmlNode, "true") !== false) + return; + + if (this.dispatchEvent("beforecheck", {xmlNode : xmlNode}) === false) + return false; + + if (!this.multicheck && this.$checkedList.length) + this.clearChecked(true); + + this.$checkedList.push(xmlNode); + + + if (this.$isTreeArch) { + //Children + var nodes = xmlNode.selectNodes(".//" + + this.each.split("|").join("|.//")); + + this.checkList(nodes, null, true, true); + + //Parents + var all, pNode = this.getTraverseParent(xmlNode); + while(pNode && pNode != this.xmlRoot) { + nodes = this.getTraverseNodes(pNode); + + all = true; + for (var i = 0; i < nodes.length; i++) { + if (this.$checkedList.indexOf(nodes[i]) == -1) { + all = false; + break; + } + } + + apf.setStyleClass(apf.xmldb.getHtmlNode(pNode, this), + all ? "checked" + : "partial", ["partial", "checked"]); + + if (all) //logical assumption that parent cannot be selected at this point + this.$checkedList.push(pNode); + + pNode = this.getTraverseParent(pNode); + } + } + + + this.$setStyleClass(apf.xmldb.getHtmlNode(xmlNode, this), + "checked", ["partial"]); + + this.dispatchEvent("aftercheck", { + list : this.$checkedList, + xmlNode : xmlNode + }); + }; + + /** + * Unchecks a single, or set of. + * + * @param {mixed} xmlNode the identifier to determine the selection. + * @return {Boolean} whether the selection could be made + * + * @event beforeuncheck Fires before a uncheck is made + * + * @event afteruncheck Fires after a uncheck is made + * + */ + this.uncheck = function(xmlNode, userAction){ + if (userAction && this.disabled + || this.$checkedList.indexOf(xmlNode) == -1) + return; + + if (userAction + && this.$executeSingleValue("check", "checked", xmlNode, "false") !== false) + return; + + + if (this.$isTreeArch) + return this.checkList([xmlNode], true, true); + + + if (this.dispatchEvent("beforeuncheck", { + xmlNode : xmlNode + }) === false) + return false; + + this.$checkedList.remove(xmlNode); + this.$setStyleClass(apf.xmldb.getHtmlNode(xmlNode, this), + "", ["checked", "partial"]); + + this.dispatchEvent("afteruncheck", { + list : this.$checkedList, + xmlNode : xmlNode + }); + }; + + /** + * Toggles between check and uncheck a single, or set of. + * + * @param {mixed} xmlNode the identifier to determine the selection. + * + */ + this.checkToggle = function(xmlNode, userAction){ + if (userAction && this.disabled) + return; + + if (xmlNode.style) { + var htmlNode = xmlNode, + id = htmlNode.getAttribute(apf.xmldb.htmlIdTag); + while (!id && htmlNode.parentNode) + id = (htmlNode = htmlNode.parentNode) + .getAttribute(apf.xmldb.htmlIdTag); + xmlNode = apf.xmldb.getNode(htmlNode) + } + + if (this.$checkedList.indexOf(xmlNode) > -1) + this.uncheck(xmlNode, userAction); + else + this.check(xmlNode, userAction); + }; + + /** + * Checks a set of items + * + * @param {Array} xmlNodeList the {@link term.datanode data nodes} that will be selected. + * @param {boolean} uncheck + * @param {boolean} noClear + * @param {boolean} noEvent whether to not call any events + * @event beforecheck Fires before a check is made + * object: + * {XMLElement} xmlNode the {@link term.datanode data node} that will be deselected. + * @event aftercheck Fires after a check is made + * object: + * {XMLElement} xmlNode the {@link term.datanode data node} that is deselected. + */ + this.checkList = function(xmlNodeList, uncheck, noClear, noEvent, userAction){ + //if (apf.isIE < 8) + if (!xmlNodeList.indexOf) + xmlNodeList = apf.getArrayFromNodelist(xmlNodeList); + //@todo is this need for ie8 and/or other browsers + + if (userAction){ + if (this.disabled) + return; + + var changes = []; + for (var c, i = 0; i < xmlNodeList.length; i++) { + c = this.$executeSingleValue("check", "checked", xmlNodeList[i], uncheck ? "false" : "true", true) + if (c === false) break; + changes.push(c); + } + + if (changes.length) { + return this.$executeAction("multicall", changes, "checked", + xmlNodeList[0], null, null, + xmlNodeList.length > 1 ? xmlNodeList : null); + } + } + + if (userAction && this.disabled) return; + + if (!noEvent && this.dispatchEvent("beforecheck", { + list : xmlNodeList + }) === false) + return false; + + if (!uncheck && !noClear) + this.clearChecked(true); + + if (!this.multicheck) + xmlNodeList = [xmlNodeList[0]]; + + var i; + if (uncheck) { + for (i = xmlNodeList.length - 1; i >= 0; i--) { + this.$checkedList.remove(xmlNodeList[i]); + this.$setStyleClass( + apf.xmldb.getHtmlNode(xmlNodeList[i], this), "", ["checked"]); + } + } + else { + for (i = xmlNodeList.length - 1; i >= 0; i--) { + this.$checkedList.push(xmlNodeList[i]); + this.$setStyleClass( + apf.xmldb.getHtmlNode(xmlNodeList[i], this), "checked"); + } + } + + + if (!noEvent && this.$isTreeArch) { + var _self = this; + function recur(xmlNode, forceChange) { + var nodes = _self.getTraverseNodes(xmlNode); + if (!nodes.length) { + if (forceChange) { + if (uncheck) { + _self.$checkedList.remove(xmlNode); + _self.$setStyleClass(apf.xmldb.getHtmlNode(xmlNode, _self), + "", ["checked"]); + return 0; + } + else { + if (_self.$checkedList.indexOf(xmlNode) == -1) { + _self.$checkedList.push(xmlNode); + _self.$setStyleClass( + apf.xmldb.getHtmlNode(xmlNode, _self), "checked"); + } + return 1; + } + } + return _self.$checkedList.indexOf(xmlNode) > -1 ? 1 : 0; + } + + var isInList = _self.$checkedList.indexOf(xmlNode) != -1, + shouldBeChanged = forceChange + || xmlNodeList.indexOf(xmlNode) > -1 && (uncheck + ? !isInList + : isInList), + all = true, + none = true, + partial = false, + isChecked; + for (var i = nodes.length - 1; i >= 0; i--) { + isChecked = recur(nodes[i], shouldBeChanged); + if (isChecked) { + none = false; + if (!partial && isChecked == 2) { + partial = true; + //break; + } + } + else + all = false; + if (!all && !none) { + partial = true; + //break; + } + } + + if (xmlNode == _self.xmlRoot) + return; + + if (all) { + if (!isInList) { + _self.$checkedList.push(xmlNode); + apf.setStyleClass(apf.xmldb.getHtmlNode(xmlNode, _self), + "checked", ["partial"]); + } + } + else{ + if (isInList) + _self.$checkedList.remove(xmlNode); + + apf.setStyleClass(apf.xmldb.getHtmlNode(xmlNode, _self), + partial ? "partial" : "", ["partial", "checked"]); + } + + return all ? 1 : (none ? 0 : 2); + } + + recur(this.xmlRoot) + } + + + if (!noEvent) + this.dispatchEvent("aftercheck", { + list : xmlNodeList + }); + }; + + /** + * Removes the selection of one or more checked nodes. + * + * @param {Boolean} [singleNode] whether to only deselect the indicated node + * @param {Boolean} [noEvent] whether to not call any events + * @event beforeuncheck Fires before a uncheck is made + * object: + * {XMLElement} xmlNode the {@link term.datanode data node} that will be deselected. + * @event afteruncheck Fires after a uncheck is made + * object: + * {XMLElement} xmlNode the {@link term.datanode data node} that is deselected. + */ + this.clearChecked = function(noEvent){ + if (!noEvent && this.dispatchEvent("beforeuncheck", { + xmlNode : this.$checkedList + }) === false) + return false; + + for (var i = this.$checkedList.length - 1; i >= 0; i--) { + this.$setStyleClass( + apf.xmldb.getHtmlNode(this.$checkedList[i], this), "", ["checked"]); + } + + this.$checkedList.length = 0; + + if (!noEvent) { + this.dispatchEvent("afteruncheck", { + list : this.$checkedList + }); + } + }; + + /** + * Determines whether a node is checked. + * + * @param {XMLElement} xmlNode The {@link term.datanode data node} to be checked. + * @return {Boolean} whether the element is selected. + */ + this.isChecked = function(xmlNode){ + return this.$checkedList.indexOf(xmlNode) > -1; + }; + + /** + * Retrieves an array or a document fragment containing all the checked + * {@link term.datanode data nodes} from this element. + * + * @param {Boolean} [xmldoc] whether the method should return a document fragment. + * @return {mixed} the selection of this element. + */ + this.getChecked = function(xmldoc){ + var i, r; + if (xmldoc) { + r = this.xmlRoot + ? this.xmlRoot.ownerDocument.createDocumentFragment() + : apf.getXmlDom().createDocumentFragment(); + for (i = 0; i < this.$checkedList.length; i++) + apf.xmldb.cleanNode(r.appendChild( + this.$checkedList[i].cloneNode(true))); + } + else { + for (r = [], i = 0; i < this.$checkedList.length; i++) + r.push(this.$checkedList[i]); + } + + return r; + }; + + /** + * Checks all the {@link term.eachnode each nodes} of this element + * + */ + this.checkAll = function(userAction){ + if (!this.multicheck || userAction && this.disabled || !this.xmlRoot) + return; + + var nodes = this.$isTreeArch + ? this.xmlRoot.selectNodes(".//" + + this.each.split("|").join("|.//")) + : this.getTraverseNodes(); + + this.checkList(nodes); + }; + + this.addEventListener("beforeload", function(){ + if (!this.$hasBindRule("checked")) //only reset state when check state isnt recorded + this.clearChecked(true); + }); + + this.addEventListener("afterload", function(){ + if (!this.$hasBindRule("checked") && this.$checkedList.length) //only reset state when check state isnt recorded + this.checkList(this.$checkedList, false, true, false); //@todo could be optimized (no event calling) + }); + + this.addEventListener("xmlupdate", function(e){ + if (e.action == "attribute" || e.action == "text" + || e.action == "synchronize" || e.action == "update") { + //@todo list support! + var c1 = apf.isTrue(this.$applyBindRule("checked", e.xmlNode)); + var c2 = this.isChecked(e.xmlNode); + if (c1 != c2) { + if (c1) { + this.check(e.xmlNode); + } + else { + this.uncheck(e.xmlNode); + } + } + } + }); + + + this.addEventListener("aftercheck", function(){ + //@todo inconsistent because setting this is in event callback + if (this.checklength != this.$checkedList.length) + this.setProperty("checklength", this.$checkedList.length); + }); + + this.addEventListener("afteruncheck", function(){ + //@todo inconsistent because setting this is in event callback + if (this.checklength != this.$checkedList.length) + this.setProperty("checklength", this.$checkedList.length); + }); + +}; + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/core/baseclasses/teleport.js)SIZE(8790)TIME(Fri, 14 Oct 2011 08:01:44 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + + +/** + * Element which specifies the ways the application can communicate to remote + * data sources. + * Example: + * Example of the {@link teleport.cgi rpc module with the cgi protocol}. + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * //This function is called when the search returns + * function processSearch(data, state, extra){ + * alert(data) + * } + * + * //Execute a search for the product car + * comm.searchProduct('car', 10); + * + * + * Example: + * Example of the {@link teleport.soap rpc module with the soap protocol}. + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * //This function is called when the search returns + * function processSearch(data, state, extra){ + * alert(data) + * } + * + * //Execute a search for the product car + * comm.searchProduct('car', 10); + * + * + * Example: + * Writing to a file with a WebDAV connector + * + * + * + * + * // write the text 'bar' to a file on the server called 'foo.txt' + * myWebDAV.write('http://my-webdav-server.com/dav_files/foo.txt', 'bar'); + * + * + * Example: + * XMPP connector with new message notification + * + * + * + * + * // This function is called when a message has arrived + * function messageReceived(from){ + * alert('Received message from ' + from); + * } + * + * // Send a message to John + * myXMPP.sendMessage('john@my-jabber-server.com', 'A test message', '', + * apf.xmpp.MSG_CHAT); + * + * + * + * @attribute {String} url the location of the server that is + * recipient of the rpc messages. + * @attribute {String} [route-server] String specifying the url to the route script. + * Remarks: + * The route script will receive the route information in 3 extra headers: + * X-Route-Request - Containing the destination url.
+ * X-Proxy-Request - Containing the destination url.
+ * X-Compress-Response - Set to 'gzip'.
+ * @attribute {Boolean} [autoroute] whether the call should be routed + * through a proxy when a permission + * error occurs due to the same domein policy. + * @attribute {Number} [timeout] the number of milliseconds after + * which the call is considered timed out. + * + * + * @define teleport + * @addnode global + * @allowchild {teleport} + * + * @default_private + */ +apf.Teleport = function(){ + this.$init(true); +}; + +apf.__TELEPORT__ = 1 << 28; + +(function() { + this.$parsePrio = "002"; + + this.$regbase = this.$regbase | apf.__TELEPORT__; + + this.$booleanProperties["autoroute"] = true; + + this.$supportedProperties.push("url", "timeout", "protocol", "route-server", + "autoroute"); + + this.$propHandlers["url"] = function(value) { + var url = new apf.url(value); + + // do some extra startup/ syntax error checking + if (!url.protocol) { + return apf.console.error(apf.formatErrorString(0, this, + "Communication (Teleport) initialization error", + "Invalid server url provided: '" + value + "'")); + } + + this.$host = url.host; + this.$rootPath = url.path; + this.$server = value.replace(new RegExp(this.$rootPath + "$"), ""); + }; + + this.$propHandlers["timeout"] = function(value) { + this.timeout = parseInt(value) || 10000; + }; + + this.$propHandlers["protocol"] = function(value) { + var proto = value.toLowerCase(); + if (!apf[proto]) { + throw new Error(apf.formatErrorString(1025, null, "Teleport baseclass", + "Could not find Ajax.org Teleport RPC Component '" + proto + "'", this)); + } + this.implement(apf[proto]); + }; + + /** + * Returns a string representation of this object. + */ + this.toString = function(){ + return "[Ajax.org Teleport Component : " + (this.name || "") + + " (" + this.type + ")]"; + }; + + this.addEventListener("DOMNodeInsertedIntoDocument", function() { + var error; + if (this.type && this.type == "socket") { + // Implement Socket Module + if (!apf.socket) + error = "Socket"; + else + this.implement(apf.socket); + } + else { + // Implement HTTP Module + if (!apf.http) + error = "HTTP"; + else + this.implement(apf.http); + } + if (error) { + throw new Error(apf.formatErrorString(1024, null, "Teleport baseclass", + "Could not find Ajax.org Teleport " + error + " Component", this.$aml)); + } + + if (this.id) + apf.$asyncObjects[this.id] = 1; + }); +}).call(apf.Teleport.prototype = new apf.AmlElement()); + + + + + +apf.Init.run("teleport"); + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/core/baseclasses/transaction.js)SIZE(23494)TIME(Fri, 14 Oct 2011 08:01:44 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + +apf.__TRANSACTION__ = 1 << 3; + + + +/** + * All elements inheriting from this {@link term.baseclass baseclass} have transaction support. A transaction is a + * set of changes to data which are treated as one change. When one of the + * changes in the set fails, all the changes will be cancelled. In the case of + * a gui this is mostly relevant when a user decides to cancel after + * making several changes. A good example are the well known property windows + * with an ok, cancel and apply button. + * + * When a user edits data, for instance user information, all the changes are + * seen as one edit and put on the undo stack as a single action. Thus clicking + * undo will undo the entire transaction, not just the last change done by that + * user in the edit window. Transaction support both optimistic and pessimistic + * locking. For more information on the latter see the first example below. + * Example: + * This example shows a list with one item. When double clicked on the item + * a window shows that allows the user to edit the properties of this item. + * When the window is closed the changes are committed to the xml data. If the + * user presses cancel the changes are discarded. By pressing the 'add new item' + * button the same window appears which allows the user to add a new item. All + * changes made by the user are also sent to the original data source via + * rpc calls. When the user starts editing an existing item a lock is requested. + * This is not necesary for transaction support, but this example shows that it + * is possible. When the lock fails the window will close. By hooking the + * 'lockfail' event the user can be notified of the reason. For more information + * see {@link term.locking}. + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * message + * + * + * + * + * Add new item + * + * + * Name + * + * Subject + * + * + * Message + * + * + * OK + * Cancel + * Apply + * + * + * + * @constructor + * @baseclass + * + * @inherits apf.StandardBinding + * @inherits apf.DataAction + * + * @event transactionconflict Fires when data in a transaction is being updated by an external process. + * + * @author Ruben Daniels (ruben AT ajax DOT org) + * @version %I%, %G% + * @since 0.8.9 + */ +apf.Transaction = function(){ + this.$regbase = this.$regbase | apf.__TRANSACTION__; + + this.$addParent = + this.$transactionNode = + this.$transactionSubject = + this.$originalNode = + this.$inTransaction = + this.$lastAction = null; + + this.$supportedProperties.push("autoshow"); + + /** + * @attribute {Boolean} autoshow whether this element is shown when a transaction begins. + */ + this.$booleanProperties["autoshow"] = true; + + /** + * Commits a started transaction. This will trigger an update or add action. + * forked copy of template data. + * + * @todo check what's up with actiontracker usage... + * @bug when a commit is cancelled using the onbeforecommit event, the + * state of the element becomes undefined. + */ + this.commit = function(repeat){ + if (!this.$inTransaction) + return false; + + if (!this.$validgroup && this.validgroup) + this.$validgroup = self[this.validgroup]; + + if (this.$validgroup && !this.$validgroup.isValid()) + return false; + + var returnValue = true; + if (!this.$at.undolength) { + if (repeat) + return false; + + this.$at.purge(); + this.$inTransaction = false; + + this.load(this.$originalNode); + this.$helperModel.reset(); + + returnValue = false; + } + else { + + + this.$at.reset();//purge(); + this.$inTransaction = false; + + //@todo recursive + this.$transactionNode.removeAttribute(apf.xmldb.xmlListenTag) + + if (this.$lastAction == "add") { + //Use ActionTracker :: this.xmlData.selectSingleNode("DataBinding/@select") ? o.xmlRoot : o.selected + if (this.$transactionSubject.$executeAction("appendChild", + [this.$addParent, this.$transactionNode], "add", this.$transactionNode) + && this.$transactionSubject.hasFeature(apf.__MULTISELECT__)) { + this.$transactionSubject.select(this.$transactionNode); + } + + this.$transactionSubject = null; + } + else { + //Use ActionTracker + //getTraverseParent(o.selected) || o.xmlRoot + var at = this.$at; + this.$at = this.dataParent + ? this.dataParent.parent.getActionTracker() + : null;//self[this.getAttribute("actiontracker")];//this.dataParent.parent.getActionTracker(); + + this.$transactionSubject.$executeAction("replaceNode", [this.$originalNode, this.$transactionNode], + "update", this.$transactionNode); + + this.$at = at; + + //this.load(this.$transactionNode); + } + } + + this.$transactionNode = null; + this.$addParent = null; + this.$originalNode = null; + + if (this.autoshow) { + if (this.autoshow == -1) + this.autoshow = true; + else + this.hide(); + } + + return returnValue; + }; + + /** + * Rolls back the started transaction. + */ + this.rollback = function(noLoad){ + if (!this.$inTransaction) + return; + + + + if (this.$at) { + if (this.rpcMode == "realtime") + this.$at.undo(-1); + + this.$at.reset(); + } + //this.xmldb.reset(); + + this.$transactionNode = null; //prevent from restarting the transaction in load + this.$addParent = null; + + //Cleanup + if (!noLoad) + this.load(this.$originalNode); + + this.$helperModel.reset(); + + this.$stopAction(this.$lastAction, true); + + this.$originalNode = null; + this.$inTransaction = false; + + if (this.autoshow) { + if (this.autoshow == -1) + this.autoshow = true; + else + this.hide(); + } + }; + + /** + * Starts a transaction for this element. This is either an add or update. + * @param {String} strAction the type of transaction to start + * Possible values: + * add the transaction is started to add a new {@link term.datanode data node}. + * update the transaction is started to update an existing {@link term.datanode data node}. + * @param {XMLElement} xmlNode + * @param {XMLElement} parentXmlNode + * @param {AMLElement} dataParent + */ + this.begin = function(strAction, xmlNode, parentXmlNode, dataParent){ + if (this.$inTransaction) { + /*throw new Error(apf.formatErrorString(0, this, + "Starting Transaction", + "Cannot start a transaction without committing or rolling \ + back previously started transaction.", this.oldRoot));*/ + + + + if (this.autoshow) + this.autoshow = -1; + this.rollback(); + } + + + + //Add should look at dataParent and take selection or xmlRoot + //winMail.dataParent.parent.xmlRoot + + var _self = this; + this.$lastAction = strAction; + + if (!this.$lastAction) { + this.$lastAction = this.xmlRoot && "update" || "add"; + /*this.actionRules && (this.actionRules.add + ? "add" + : (this.actionRules.update + ? "update" + : null)) || this.xmlRoot && "update";*/ + } + + + + //Determines the actiontracker to integrate the grouped action into + if (dataParent) + this.$setDynamicProperty("model", "[" + dataParent.id + ".selected]"); //@todo what if it doesn't have an id + + if (xmlNode && this.$lastAction == "update") { + this.xmlRoot = xmlNode; + //this.$inTransaction = -1; //Prevent load from triggering a new transaction + //this.load(xmlNode); + } + + /* + * @todo: + * create actiontracker based on data id, destroy actiontracker on cancel/commit - thus being able to implement editor feature natively + * Multiple transactions can exist at the same time in the same container, but on different data + * .cancel(xmlNode) .apply(xmlNode) + * .list(); // returns a list of all started transactions + * Add undo/redo methods to winMultiEdit + * Route undolength/redolength properties + * Setting replaceat="start" or replaceat="end" + */ + if (!this.$at) { + this.$at = new apf.actiontracker(); + var propListen = function(e){ + _self.setProperty(e.prop, e.value); + }; + this.$at.addEventListener("prop.undolength", propListen); + this.setProperty("undolength", 0); + this.$at.addEventListener("prop.redolength", propListen); + this.setProperty("redolength", 0); + } + if (!this.$helperModel) { + this.$helperModel = new apf.model(); + this.$helperModel["save-original"] = true; + this.$helperModel.load(""); + } + + this.$transactionNode = null; + this.$addParent = null; + this.$originalNode = this.xmlRoot; + + + + this.$inTransaction = true; + function begin(){ + + + this.$inTransaction = -1; + this.$helperModel.data.appendChild(this.$transactionNode);//apf.xmldb.cleanNode()); + this.load(this.$helperModel.data.firstChild); + this.$inTransaction = true; + + if (this.disabled) + this.enable(); + + if (this.autoshow) { + if (this.autoshow == -1) + this.autoshow = true; + else + this.show(); + } + } + + //Determine data parent + dataParent = this.dataParent && this.dataParent.parent; + + if (!dataParent || !dataParent.$actions + || !dataParent.$actions[this.$lastAction]) { + dataParent = this; + } + + //Add + if (this.$lastAction == "add") { + //Check for add rule on data parent + var rule, actionRules = dataParent.$actions; + if (actionRules) { + if (xmlNode && xmlNode.nodeType) + rule = actionRules.getRule("add", xmlNode); + else if (typeof xmlNode == "string") { + if (xmlNode.trim().charAt(0) == "<") { + xmlNode = apf.getXml(xmlNode); + rule = actionRules.getRule("add", xmlNode) + } + else { + var rules = actionRules.$rules["add"]; + for (var i = 0, l = rules.length; i < l; i++) { + if (rules[i].getAttribute("type") == xmlNode) { + xmlNode = null; + rule = rules[i]; + break; + } + } + } + } + + if (!rule) + rule = (dataParent.$actions["add"] || {})[0]; + } + else + rule = null; + + + + //Run the add code (copy from multiselect) but don't add until commit + var refNode = this.$isTreeArch ? this.selected || this.xmlRoot : this.xmlRoot; + var callback = function(addXmlNode, state, extra){ + if (state != apf.SUCCESS) { + var oError; + + oError = new Error(apf.formatErrorString(1032, dataParent, + "Loading xml data", + "Could not add data for control " + dataParent.name + + "[" + dataParent.tagName + "] \nUrl: " + extra.url + + "\nInfo: " + extra.message + "\n\n" + xmlNode)); + + if (extra.tpModule.retryTimeout(extra, state, dataParent, oError) === true) + return true; + + throw oError; + } + + /*if (apf.supportNamespaces && node.namespaceURI == apf.ns.xhtml) { + node = apf.getXml(node.xml.replace(/xmlns\=\"[^"]*\"/g, "")); + //@todo import here for webkit? + }*/ + + if (typeof addXmlNode != "object") + addXmlNode = apf.getXmlDom(addXmlNode).documentElement; + if (addXmlNode.getAttribute(apf.xmldb.xmlIdTag)) + addXmlNode.setAttribute(apf.xmldb.xmlIdTag, ""); + + if (!dataParent.$startAction("add", addXmlNode, _self.rollback)) + return false; + + var actionNode = (dataParent.$actions && + dataParent.$actions.getRule("add", dataParent.$isTreeArch + ? dataParent.selected + : dataParent.xmlRoot) || {})[2]; + + if (parentXmlNode) { + _self.$addParent = parentXmlNode; + } + else if (actionNode && actionNode.getAttribute("parent")) { + _self.$addParent = dataParent.xmlRoot + .selectSingleNode(actionNode.getAttribute("parent")); + } + else { + _self.$addParent = dataParent.$isTreeArch + ? dataParent.selected || dataParent.xmlRoot + : dataParent.xmlRoot + } + + if (!_self.$addParent) + _self.$addParent = dataParent.xmlRoot || dataParent.getModel(true).data; + + if (apf.isWebkit && _self.$addParent.ownerDocument != addXmlNode.ownerDocument) + addXmlNode = _self.$addParent.ownerDocument.importNode(addXmlNode, true); //Safari issue not auto importing nodes + + _self.$transactionNode = addXmlNode; + _self.$transactionSubject = dataParent; + begin.call(_self); + } + + if (xmlNode) + return callback(xmlNode, apf.SUCCESS); + else { + if (rule && rule.get) + return apf.getData(rule.get, {xmlNode: refNode, callback: callback}) + else { + + } + } + } + + //Update + else { + if (!dataParent.$startAction(this.$lastAction, this.xmlRoot, this.rollback)) + return false; + + this.$transactionSubject = dataParent; + this.$transactionNode = this.$originalNode.cloneNode(true);//xmldb.cleanNode(this.xmlRoot.cloneNode(true)); + //xmlNode.removeAttribute(xmldb.xmlIdTag); + + //@todo rename listening attributes + begin.call(this); + } + }; + + //Transaction nodes can always load data + this.$canLoadData = function(){ + return true; + } + + //Prevent model inheritance to the children + this.addEventListener("prop.model", function(e){ + return false; + }); + + //Prevent clear dynamic + this.clear = function(){ + this.documentId = this.xmlRoot = this.cacheId = null; + } + + //No need to restart the transaction when the same node is loaded + this.addEventListener("beforeload", function(e){ + var xmlNode = e.xmlNode; + + //@todo apf3.0 test if this can be enabled again + //if (this.$originalNode == xmlNode) + //return false; + + if (this.$inTransaction == -1) + return; + + if (this.$inTransaction) { + if (this.$transactionNode && xmlNode != this.$transactionNode) { + if (this.autoshow) + this.autoshow = -1; + + this.rollback(true); + } + else return; + } + + if (this.autoshow) + this.autoshow = -1; + + if (this.begin("update", xmlNode) !== false) + return false; + }); + + //hmm really? + //@todo what to do here? check original cloned node??? + /*this.addEventListener("xmlupdate", function(e){ + if (this.$inTransaction) { + this.dispatchEvent("transactionconflict", { + action : e.action, + xmlNode: e.xmlNode, + UndoObj: e.UndoObj, + bubbles : true + }); + } + });*/ + + //@todo add when not update??? + /*this.watch("visible", function(id, oldval, newval){ + if (!this.xmlRoot || oldval == newval) + return; + + if (newval) { + if (!this.$inTransaction) + this.begin(); + } + else { + if (this.$inTransaction) + this.rollback(); + } + });*/ +} + +/** + * @attribute {Boolean} transaction Whether this element provides transaction + * support for all it's children. + * @see baseclass.transaction + */ +apf.GuiElement.propHandlers["transaction"] = function(value){ + if (!(this.transaction = apf.isTrue(value))) + return; + + if (!this.hasFeature(apf.__DATABINDING__)) + this.implement(apf.StandardBinding); + + if (!this.hasFeature(apf.__DATAACTION__)) { + this.implement(apf.DataAction); + + if (this.actions) + this.$propHandlers["actions"].call(this, this.actions, "actions"); + } + + if (!this.hasFeature(apf.__TRANSACTION__)) { + this.implement(apf.Transaction); + + if (!this.validgroup) { + this.$validgroup = new apf.ValidationGroup(); + this.$validgroup.register(this); + } + + if (!this.id) + this.setProperty("id", this.localName + "_" + this.$uniqueId); + + var attr = this.attributes.getNamedItem("model"); + if (!attr) //@todo find a way to not have to add a model + this.attributes.push(attr = new apf.AmlAttr(this, "model", null)); + attr.inheritedValue = "{" + this.id + ".root}"; + + if (typeof this.autoshow == "undefined" + && (this.localName == "modalwindow" || this.localName == "window")) + this.autoshow = true; + } +} + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/core/baseclasses/virtualviewport.js)SIZE(28823)TIME(Fri, 14 Oct 2011 08:01:44 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + +apf.__VIRTUALVIEWPORT__ = 1 << 19; + + + +/** + * All elements inheriting from this {@link term.baseclass baseclass} can have a virtual viewport. + * + * @experimental This code has never been run. + * @constructor + * @baseclass + * @private + * + * @author Ruben Daniels (ruben AT ajax DOT org) & Mike de Boer + * @version %I%, %G% + * @since 1.0 + */ +apf.VirtualViewport = function(){ + this.$init(true); + + this.$regbase = this.$regbase | apf.__VIRTUALVIEWPORT__; + + this.virtualVTimer = null; + this._xmlUpdate = this.$xmlUpdate; + + apf.setStyleClass(this.$ext, "virtual"); + + this.$deInitNode = function(xmlNode, htmlNode){ + /* + Not the htmlNode is deleted, but the viewport is rerendered from this node on. + If viewport is too high either the render starting point is adjusted and + a complete rerender is requested, or the last empty elements are hidden + */ + this.viewport.redraw();//very unoptimized + }; + + this.$moveNode = function(xmlNode, htmlNode){ + /* + Do a remove when removed from current viewport + Do an add when moved to current viewport + Do a redraw from the first of either when both in viewport + */ + this.viewport.redraw();//very unoptimized + }; + + this.emptyNode = apf.xmldb.getXml(""); + this.$addEmpty = this.$add; + this.$add = function(xmlNode, Lid, xmlParentNode, htmlParentNode, beforeNode){ + //find new slot + var htmlNode = this.$pHtmlDoc.getElementById(Lid); + + if(!htmlNode) + return; + + //execute update + this.$updateNode(xmlNode, htmlNode);//, noModifier); + }; + + this.$fill = function(){ + + }; + + this.addEventListener("$load", function(){ + if (!this.viewport.limit) + this.viewport.limit = 1; + }); + + this.clear = function(nomsg, do_event){ + if (this.clearSelection) + this.clearSelection(!do_event); + + this.documentId = this.xmlRoot = this.cacheId = null; + + if (!nomsg) { + this.viewport.offset = 0; + this.viewport.length = 0; + this.viewport.resize(0); + this.viewport.sb.update(); + + this.$setClearMessage(this["empty-message"]); + } + else if(this.$removeClearMessage) + this.$removeClearMessage(); + + this.viewport.cache = null; + }; + + var _self = this; + this.viewport = { + offset : 0, + limit : 2, + length : 0, + sb : new apf.scrollbar(), + host : this, + cache : null, + + inited : false, + draw : function(){ + this.inited = true; + var limit = this.limit; this.limit = 0; + this.resize(limit, true); + }, + + redraw : function(){ + this.change(this.offset); + }, + + // set id's of xml to the viewport + prepare : function(){ + if (!this.inited) + this.draw(); + + var nodes = _self.getTraverseNodes(); + if (!nodes) + return; + + var docId = apf.xmldb.getXmlDocId(_self.xmlRoot), + hNodes = _self.$container.childNodes; + for (var j = 0, i = 0, l = hNodes.length; i < l; i++) { + if (hNodes[i].nodeType != 1) continue; + + hNodes[i].style.display = (j >= nodes.length) ? "none" : "block"; //Will ruin tables & lists + + apf.xmldb.nodeConnect(docId, nodes[j], hNodes[i], _self); + j++; + } + }, + + /** + * @note This function only supports single dimension items (also no grid, like thumbnails) + */ + resize : function(limit, updateScrollbar){ + this.cache = null; + + var i; + //Viewport shrinks + if (limit < this.limit) { + var nodes = _self.$container.childNodes; + for (i = nodes.length - 1; i >= 0; i--) { + if (nodes[i].nodeType != 1) continue; + _self.$container.removeChild(nodes[i]); + if (--this.limit == limit) break; + } + } + //Viewport grows + else if (limit > this.limit) { + for (i = this.limit; i < limit; i++) { + _self.$addEmpty(_self.emptyNode, "", _self.xmlRoot, _self.$container); + } + } + else + return; + + this.limit = limit; + + if (updateScrollbar) + this.sb.update(this.$container); + }, + + findNewLimit : function(scrollTop){ + var oHtml = _self.$container; + + if (!scrollTop) + scrollTop = oHtml.scrollTop; + + if (!_self.xmlRoot || oHtml.lastChild && oHtml.lastChild.style.display == "none") + return; + + //Grow + if (!oHtml.lastChild || oHtml.lastChild.offsetTop + oHtml.lastChild.offsetHeight <= oHtml.offsetHeight + scrollTop) { + var Lid, xmlNode, nodes, sel = _self.$getSelection(); + while (this.limit < this.length - 1 && (!oHtml.lastChild || oHtml.lastChild.offsetTop + oHtml.lastChild.offsetHeight <= oHtml.offsetHeight + scrollTop)) { + this.limit++; + + nodes = _self.getTraverseNodes(); + if (nodes.length < this.limit) { + this.limit = nodes.length; + break; + } + + xmlNode = nodes[nodes.length - 1]; + Lid = apf.xmldb.nodeConnect(_self.documentId, xmlNode, null, _self); + _self.$addEmpty(xmlNode, Lid, _self.xmlRoot, oHtml); + if (sel.indexOf(xmlNode) > -1) + _self.$select(oHtml.lastChild); + else + _self.$deselect(oHtml.lastChild); + } + } + //Shrink + else if (oHtml.lastChild && oHtml.lastChild.offsetTop > oHtml.offsetHeight + scrollTop) { + var lastChild; + while (this.limit > 2 && (lastChild = oHtml.lastChild).offsetTop > oHtml.offsetHeight + scrollTop) { + _self.$container.removeChild(lastChild); + this.limit--; + } + } + + if (!this.initialLimit) + this.initialLimit = this.limit; + }, + + /** + * @todo This method should be optimized by checking if there is + * overlap between the new offset and the old one + */ + change : function(offset, limit, updateScrollbar, noScroll){ + var offsetN; + + if (offset < 0) + offset = 0; + + if (offset > this.length - this.limit - 1) + offsetN = Math.floor(this.length - this.limit - 1); + else + offsetN = Math.floor(offset); + + if (!limit) + limit = this.limit; + + //var offsetN = Math.floor(offset); + + this.cache = null; + var diff = offsetN - this.offset, + oldLimit = this.limit; + if (diff * diff >= this.limit*this.limit) //there is no overlap + diff = false; + this.offset = offsetN; + + if (diff > 0) { //get last node before resize + var lastNode = _self.$container.lastChild; + if (lastNode.nodeType != 1) + lastNode = lastNode.previousSibling; + } + + /*if (limit && this.limit != limit) + this.resize(limit, updateScrollbar); + else */ + if (updateScrollbar) { + this.sb.$curValue = this.offset / (this.length - this.limit - 1); + this.sb.updatePos(); + } + + //this.viewport.prepare(); + + //Traverse through XMLTree + //var nodes = this.$addNodes(this.xmlRoot, this.$container, null, this.renderRoot); + var nodes = _self.getTraverseNodes(); + if (!nodes) + return; + + if (nodes.length < this.limit) { + if (offset > 0) + alert("shouldnt get here"); + else + this.resize(nodes.length); + } + + var docId = apf.xmldb.getXmlDocId(_self.xmlRoot), + hNodes = _self.$container.childNodes, + xmlNode, htmlNode, xmlPos, sel, len, j, i; + + //remove nodes from the beginning + if (diff > 0) { + xmlPos = oldLimit - diff; + len = hNodes.length, + sel = _self.$getSelection(); + for (j = 0, i = 0; j < diff && i < len; i++) { + htmlNode = _self.$container.firstChild; + if (htmlNode.nodeType == 1) { + j++; + xmlNode = nodes[xmlPos++]; + if (xmlNode) { + apf.xmldb.nodeConnect(docId, xmlNode, htmlNode, _self); + _self.$updateNode(xmlNode, htmlNode);//, noModifier); + if (sel.indexOf(xmlNode) > -1) + _self.$select(htmlNode); + else + _self.$deselect(htmlNode); + htmlNode.style.display = "block"; + } + else { + htmlNode.style.display = "none"; + } + } + + _self.$container.appendChild(htmlNode); + } + + //var lastNode = nodes[oldLimit - diff - 1] + } + //remove nodes from the end + else if (diff < 0) { + diff = diff * -1; + xmlPos = 0; //should be adjusted for changing limit + sel = _self.$getSelection(); + for (j = 0, i = hNodes.length-1; j < diff && i >= 0; i++) { + htmlNode = _self.$container.lastChild; + if (htmlNode.nodeType == 1) { + j++; + xmlNode = nodes[xmlPos++]; + apf.xmldb.nodeConnect(docId, xmlNode, htmlNode, _self); + _self.$updateNode(xmlNode, htmlNode);//, noModifier); + if (sel.indexOf(xmlNode) > -1) + _self.$select(htmlNode); + else + _self.$deselect(htmlNode); + htmlNode.style.display = "block"; + } + + _self.$container.insertBefore(htmlNode, _self.$container.firstChild); + } + } + //Recalc all nodes + else if (diff === false){ + len = hNodes.length; + sel = _self.$getSelection(); + for (j = 0, i = 0; i < len; i++) { + htmlNode = hNodes[i]; + if (htmlNode.nodeType == 1) { + xmlNode = nodes[j++]; + apf.xmldb.nodeConnect(docId, xmlNode, htmlNode, _self); + _self.$updateNode(xmlNode, htmlNode);//, noModifier); + if (sel.indexOf(xmlNode) > -1) + _self.$select(htmlNode); + else + _self.$deselect(htmlNode); + } + } + } + + if (!noScroll) { + if (offset >= this.length - this.initialLimit) { + diff = offset - (this.length - this.initialLimit) + 2; + _self.$container.scrollTop = (_self.$container.scrollHeight - _self.$container.offsetHeight) * (diff / 2); + } + else { + var scrollTop = (offset % 1) * _self.$container.firstChild.offsetHeight;//(diff/limit) * _self.$container.offsetHeight; + this.findNewLimit(scrollTop); + _self.$container.scrollTop = scrollTop; + } + + if (updateScrollbar) + this.sb.update(); + + return; + } + + //Build HTML + //_self.$fill(nodes); + + /*if (_self.$selected) { + _self.$deselect(_self.$selected); + _self.$selected = null; + } + + if (_self.selected && _self.$isInViewport(_self.selected)) + _self.select(_self.selected);*/ + } + }; + + this.viewport.sb.parentNode = new apf.Class().$init(); + this.viewport.sb.parentNode.$container = this.$pHtmlNode; + this.viewport.sb.dispatchEvent("DOMNodeInsertedIntoDocument"); + + //this.$container.style.paddingLeft = this.viewport.sb.$ext.offsetWidth + "px"; + + //this.viewport.sb.realtime = false;//!apf.isIE; + this.viewport.sb.attach(this.$container, this.viewport, function(timed, pos){ + var vp = _self.viewport; + + if (vp.sb.realtime || !timed) { + var l = vp.length - vp.initialLimit; + if (l == 0) + _self.$container.scrollTop = pos * (_self.$container.scrollHeight - _self.$container.offsetHeight); + else + vp.change(l * pos, vp.limit, false); + } + else { + clearTimeout(this.virtualVTimer); + this.virtualVTimer = $setTimeout(function(){ + vp.change(Math.round((vp.length - vp.initialLimit) * pos), vp.limit, false); + }, 300); + } + }); + + /* @todo + * - Fix bug in optimization + * - Fix flickering with larger viewport + * - Get templates to work + * - Firefox has problems with the scrollbar + * / Fix scrolling of items bigger than viewport (limit is too tight sometimes) + * - Improve pgup/pgdown + * - Fix multigrid lists (thumbnail) + * - Fix FF html conversion (insertHtmlNodes) + * - Optimize grow function to use fill + */ + + apf.layout.setRules(this.$container, "scrollbar", "\ + var s = apf.all[" + this.viewport.sb.$uniqueId + "];\ + s.update();\ + ", true); + apf.layout.queue(this.$container); + + this.$isInViewport = function(xmlNode, struct){ + /*var marker = xmlNode.selectSingleNode("preceding-sibling::a_marker"); + var start = marker ? marker.getAttribute("end") : 0; + + if(!struct && this.viewport.offset + this.viewport.limit < start + 1) + return false; + + var position = start; + var nodes = (marker || xmlNode).selectNodes("following-sibling::" + + this.each.split("|").join("following-sibling::")); + + for (var i = 0; i < nodes.length; i++) { + ++position; + if (nodes[i] == xmlNode) + break; + } + + if(struct) struct.position = position; + + if(this.viewport.offset > position + || this.viewport.offset + this.viewport.limit < position) + return false; + + return true;*/ + var nodes = this.getTraverseNodes(); + for (var i = 0, l = nodes.length; i < l; i++){ + if (nodes[i] == xmlNode) + return true; + } + + return false; + }; + + this.scrollTo = function(xmlNode, last){ + var sPos = {}; + this.$isInViewport(xmlNode, sPos); + this.viewport.change(sPos.position + (last ? this.viewport.limit - 1 : 0)); + }; + + /** + * @todo this one should be optimized + */ + this.getFirstTraverseNode = function(xmlNode){ + return this.getTraverseNodes(xmlNode)[0]; + }; + + /** + * @private + */ + this.$clearVirtualDataset = function(parentNode){ + var nodes = parentNode.childNodes; + for (var i = nodes.length - 1; i >= 0; i--) + parentNode.removeChild(nodes[i]); + }; + + /** + * @private + */ + this.$createVirtualDataset = function(xmlNode, length, docId) { + var marker = xmlNode.selectSingleNode("a_marker") + || xmlNode.appendChild(xmlNode.ownerDocument.createElement("a_marker")); + marker.setAttribute("start", "0"); + + if (length) { + marker.setAttribute("end", length); + marker.setAttribute("reserved", ++this.nodeCount[docId]); + this.nodeCount[docId] += length; + } + }; + + this.$xmlUpdate = function(){ + this.viewport.cache = null; + this.viewport.length = this.xmlRoot.selectNodes(this.each).length; //@todo fix this for virtual length + this.viewport.sb.update(this.$container); + this._xmlUpdate.apply(this, arguments); + }; + + this.$load = function(XMLRoot){ + //Add listener to XMLRoot Node + apf.xmldb.addNodeListener(XMLRoot, this); + + //Reserve here a set of nodeConnect id's and add them to our initial marker + //Init virtual dataset here + + if (!this.renderRoot && !this.getTraverseNodes(XMLRoot).length) + return this.clear("loading"); + + //Initialize virtual dataset if load rule exists + if (this.$hasBindRule("load")) + this.$createVirtualDataset(XMLRoot); + + //Prepare viewport + this.viewport.cache = null; + this.viewport.length = this.xmlRoot.selectNodes(this.each).length + 1; //@todo fix this for virtual length + this.viewport.prepare(); + + //Traverse through XMLTree + var nodes = this.$addNodes(XMLRoot, null, null, this.renderRoot); + + this.viewport.sb.update(this.$container); + + //Build HTML + //this.$fill(nodes); + + //Select First Child + if (this.selectable) { + if (this.autoselect) { + if (nodes.length) + this.$selectDefault(XMLRoot); + + else + this.setProperty("selected", null); //@todo review this + + } + else { + this.clearSelection(true); + var xmlNode = this.getFirstTraverseNode(); //should this be moved to the clearSelection function? + if (xmlNode) + this.setCaret(xmlNode); + + this.setProperty("selected", null); //@todo review this + this.setProperty("chosen", null); + + } + } + + if (this.$focussable) + apf.window.hasFocus(this) ? this.$focus() : this.$blur(); + }; + + this.$loadSubData = function(){}; //We use the same process for subloading, it shouldn't be done twice + + /** + * @example + */ + this.$loadPartialData = function(marker, start, length){ + //We should have a queing system here, disabled the check for now + //if (this.$hasLoadStatus(xmlRootNode)) return; + + var loadNode, rule = this.$getBindRule("load", xmlRootNode); + if (rule && (!rule[1] || rule[1](xmlRootNode))) { + this.$setLoadStatus(xmlRootNode, "loading"); + + var mdl = this.getModel(true); + + + mdl.$insertFrom(rule.getAttribute("get"), { + + ascending : this.$sort ? this.$sort.get().ascending : true, + + xmlNode : loadNode, + documentId : this.documentId, //or should xmldb find this itself + marker : marker, + start : start, + length : length, + insertPoint : this.xmlRoot, + amlNode : this, + callback : function(xmlNode){ + + _self.setProperty("root", _self.xmlRoot); + + + var length = parseInt(apf.queryValue(xmlNode, + rule.getAttribute("total"))); + + if (_self.viewport.length != length) { + _self.viewport.length = length; + + this.$createVirtualDataset(_self.xmlRoot, + _self.viewport.length, _self.documentId); + } + } + }); + } + }; + + //Consider moving these functions to the xmldatabase selectByXpath(xpath, from, length); + function fillList(len, list, from){ + for (var i = 0; i < len; i++) + list.push(_self.documentId + "|" + (from+i)); + } + + function buildList(markers, markerId, distance, xml) { + var marker, nodes, start, + vlen = this.viewport.limit, + list = []; + + //Count from 0 + if (markerId == -1) { + nodes = xml.selectNodes(_self.each); + start = 0; + marker = markers[0]; + } + else { + //Count back from end of marker + if (distance < 0) { + fillList(Math.abs(distance), list, + parseInt(marker.getAttribute("reserved")) + parseInt(marker.getAttribute("end")) + - parseInt(marker.getAttribute("start")) + distance); + + distance = 0; + _self.$loadPartialData(marker); + + if (list.length == vlen) + return list; + } + + nodes = markers[markerId].selectNodes("following-sibling::" + + this.each.split("|").join("following-sibling::")); + start = markers[markerId].getAttribute("end"); + marker = markers[++markerId]; + } + + do { + //Add found nodes + var loop = Math.min(marker.getAttribute("start") - start, vlen);//, nodes.length + for (var i = distance; i < loop; i++) + list.push(nodes[i]); + + if (list.length == vlen) + break; + + //Add empty nodes + var mlen = parseInt(marker.getAttribute("end")) - parseInt(marker.getAttribute("start")); + fillList(Math.min(mlen, vlen - list.length), list, parseInt(marker.getAttribute("reserved"))); + + //Add code here to trigger download of this missing info + _self.$loadPartialData(marker); + + start = parseInt(marker.getAttribute("end")); + marker = markers[++markerId]; + distance = 0; + } + while (list.length < vlen && marker); + + _self.viewport.cache = list; + return list; + } + + /** + * Retrieves a nodelist containing the {@link term.datanode data nodes} which are rendered by + * this element (see each nodes, see {@link baseclass.multiselectbinding.binding.each}). + * + * @param {XMLElement} [xmlNode] the parent element on which the each query is applied. + */ + this.getTraverseNodes = function(xmlNode){ + if (!this.xmlRoot) + return; + + if (this.viewport.cache) + return this.viewport.cache; + + var start = this.viewport.offset + 1, + end = start + this.viewport.limit; + + //caching statement here + + var markers = (xmlNode || this.xmlRoot).selectNodes("a_marker"); + + //Special case for fully loaded virtual dataset + if (!markers.length) { + var list = (xmlNode || this.xmlRoot).selectNodes("(" + + this.each + ")[position() >= " + start + + " and position() < " + (end) + "]"); + + + return this.$sort ? this.$sort.apply(list) : list; + + } + + for (var i = 0; i < markers.length; i++) { + //Looking for marker that (partially) exceeds viewport's current position + if (markers[i].getAttribute("end") < start) { + //If this is the last marker, count from here + if (i == markers.length - 1) + return buildList(markers, i, start - markers[i].getAttribute("end"), + (xmlNode || this.xmlRoot)); + + continue; + } + + //There is overlap AND begin is IN marker + if (markers[i].getAttribute("start") - end <= 0 + && start >= markers[i].getAttribute("start")) + return buildList(markers, i, start - markers[i].getAttribute("end"), + (xmlNode || this.xmlRoot)); + + //Marker is after viewport, there is no overlap + else if (markers[i-1]) //Lets check the previous marker, if there is one + return buildList(markers, i-1, start - markers[i-1].getAttribute("end"), + (xmlNode || this.xmlRoot)); + + //We have to count from the beginning + else + return buildList(markers, -1, start, (xmlNode || this.xmlRoot)); + } + }; + + var baseNTS = this.getNextTraverseSelected; + this.getNextTraverseSelected = function(xmlNode, up, count){ + if (!xmlNode) + xmlNode = this.selected; + if (!count) + count = 1; + + var node = baseNTS.call(this, xmlNode, up, count); + if (node && node != xmlNode) + return node; + + //@todo treeArch support + var nodes = this.getTraverseNodes(), i = 0; + while (nodes[i] && nodes[i] != xmlNode) + i++; + + if (up) + i = -1 * (nodes.length - i - 1); + + this.viewport.change(Math.max(0, this.viewport.offset + i + + (up ? count : -1 * count)), null, true, true); + + nodes = this.getTraverseNodes(); + return nodes[up ? nodes.length - 1 : 0]; + }; + + //@todo keyboard handlers for pgup/pgdown should measure items instead of assuming fixed height + + //Init + this.caching = false; //for now, because the implications are unknown +}; + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/core/baseclasses/xforms.js)SIZE(9367)TIME(Fri, 14 Oct 2011 08:01:44 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + +apf.__XFORMS__ = 1 << 17; + + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/core/baseclasses/contenteditable/clipboard.js)SIZE(3386)TIME(Tue, 13 Dec 2011 13:33:58 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + + +apf.clipboard = new apf.Class().$init(); +apf.clipboard.store = null; +apf.clipboard.empty = true; +apf.clipboard.copied = null; +apf.clipboard.put = function(item){ + this.store = item; + this.setProperty("data", item); + this.setProperty("empty", item ? false : true); +}; +apf.clipboard.clear = function(){ + this.setProperty("data", null); + this.setProperty("empty", true); +} +apf.clipboard.get = function(){ + return this.store; +}; + + +apf.clipboard.$highlightSelection = function(amlNode, nodes, unset){ + for (var i = 0, l = nodes.length; i < l; i++) { + apf.setStyleClass(apf.xmldb.getHtmlNode(nodes[i], amlNode), (unset ? '' : 'cut'), ['cut']); + } +} +apf.clipboard.copySelection = function(amlNode){ + var nodes = this.get() || []; + this.$highlightSelection(amlNode, nodes, true); + this.put(amlNode.getSelection().map(function (node) { + return apf.xmldb.getCleanCopy(node); + })); + this.copied = true; +}; +apf.clipboard.cutSelection = function(amlNode){ + var nodes = this.get() || []; + this.$highlightSelection(amlNode, nodes, true); + this.put(nodes = amlNode.getSelection()); + this.$highlightSelection(amlNode, nodes); + this.copied = false; +}; +apf.clipboard.pasteSelection = function(amlNode, selected){ + var nodes = this.get(); + if (!nodes) return; + + if (!selected) + selected = amlNode.selected || amlNode.getFirstTraverseNode(); + + if (amlNode.hasFeature(apf.__DRAGDROP__)) { + var candrop = amlNode.isDropAllowed(apf.clipboard.data, selected); + if (!candrop) + return false; + var action = candrop[1] && candrop[1].action + || (amlNode.$isTreeArch ? "tree-append" : "list-append"); + amlNode.$dragDrop(selected, this.store, candrop && candrop[1], action, + null, null, null, true) + + //amlNode.copy(nodes, selected, undefined, !this.copied); + } + else { + if (nodes[0].parentNode) { + for (var i = 0, l = nodes.length; i < l; i++) { + apf.xmldb.moveNode(selected, nodes[i]); + } + } + else { + for (var i = 0, l = nodes.length; i < l; i++) { + apf.xmldb.appendChild(selected, nodes[i]); + } + } + } + + if (!this.copied) { + this.$highlightSelection(amlNode, nodes, true); + apf.clipboard.clear(); + } + + amlNode.selectList(nodes); +}; + + + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/core/baseclasses/contenteditable/commands.js)SIZE(30488)TIME(Fri, 14 Oct 2011 08:01:44 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/core/baseclasses/contenteditable/interactive.js)SIZE(57362)TIME(Fri, 14 Oct 2011 08:01:44 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/core/baseclasses/contenteditable/selectrect.js)SIZE(5678)TIME(Fri, 14 Oct 2011 08:01:44 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/core/baseclasses/contenteditable/visualconnect.js)SIZE(36914)TIME(Fri, 14 Oct 2011 08:01:44 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/core/baseclasses/contenteditable/visualselect.js)SIZE(18159)TIME(Fri, 14 Oct 2011 08:01:44 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/core/window-o3.js)SIZE(5461)TIME(Fri, 14 Oct 2011 08:01:44 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/core/window.js)SIZE(50596)TIME(Wed, 30 Nov 2011 17:29:20 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + + +/** + * Object representing the window of the aml application. The semantic is + * similar to that of a window in the browser, except that this window is not + * the same as the javascript global object. It handles the focussing within + * the document and several other events such as exit and the keyboard events. + * + * @event blur Fires when the browser window looses focus. + * @event focus Fires when the browser window receives focus. + * + * @constructor + * @inherits apf.Class + * @default_private + * + * @author Ruben Daniels (ruben AT ajax DOT org) + * @version %I%, %G% + * @since 0.8 + */ +apf.window = function(){ + this.$uniqueId = apf.all.push(this); + this.apf = apf; + + /** + * Returns a string representation of this object. + */ + this.toString = function(){ + return "[apf.window]"; + }; + + /** + * Retrieves the primary {@link element.actiontracker action tracker} of the application. + */ + this.getActionTracker = function(){ + return this.$at + }; + + /** + * @private + */ + this.loadCodeFile = function(url){ + //if(apf.isWebkit) return; + if (self[url]) + apf.importClass(self[url], true, this.win); + else + apf.include(url);//, this.document); + }; + + + + /** + * Show the browser window. + */ + this.show = function(){ + if (apf.isDeskrun) + jdwin.Show(); + }; + + /** + * Hide the browser window. + */ + this.hide = function(){ + if (apf.isDeskrun) { + jdwin.Hide(); + } + else { + this.loaded = false; + if (this.win) + this.win.close(); + } + }; + + /** + * Focus the browser window. + */ + this.focus = function(){ + if (apf.isDeskrun) + jdwin.SetFocus(); + else + window.focus(); + }; + + /** + * Set the icon of the browser window. + * @param {String} url the location of the .ico file. + */ + this.setIcon = function(url){ + if (apf.isDeskrun) + jdwin.icon = parseInt(url) == url ? parseInt(url) : url; + }; + + /** + * Set the title of the browser window. + * @param {String} value the new title of the window. + */ + this.setTitle = function(value){ + this.title = value || ""; + + if (apf.isDeskrun) + jdwin.caption = value; + else + document.title = (value || ""); + }; + + /** + * @private + */ + this.loadAml = function(x){ + if (x[apf.TAGNAME] == "deskrun") + this.loadDeskRun(x); + /*else { + + }*/ + }; + + + + /**** Focus Internals ****/ + + + this.vManager = new apf.visibilitymanager(); + + + + this.zManager = new apf.zmanager(); + + + + + this.$tabList = []; + + this.$addFocus = function(amlNode, tabindex, isAdmin){ + if (!isAdmin) { + amlNode.addEventListener("DOMNodeInserted", moveFocus); + amlNode.addEventListener("DOMNodeRemoved", removeFocus); + + if (amlNode.$isWindowContainer > -2) { + amlNode.addEventListener("focus", trackChildFocus); + amlNode.addEventListener("blur", trackChildFocus); + + amlNode.$focusParent = amlNode; + + if (amlNode.$isWindowContainer > -1) { + if (!amlNode.$tabList) + amlNode.$tabList = [amlNode]; + + this.$tabList.push(amlNode); + return; + } + else { + amlNode.$tabList = [amlNode]; + } + } + } + + var fParent = findFocusParent(amlNode), + list = fParent.$tabList; + + + + if (!amlNode.$isWindowContainer) + amlNode.$focusParent = fParent; + else + amlNode.$focusParent2 = fParent; + + if (list[tabindex]) + list.insertIndex(amlNode, tabindex); + else + list.push(amlNode); + }; + + this.$removeFocus = function(amlNode){ + if (!amlNode.$focusParent) + return; + + amlNode.$focusParent.$tabList.remove(amlNode); + + if (!amlNode.$isWindowContainer) { + amlNode.removeEventListener("DOMNodeInserted", moveFocus); + amlNode.removeEventListener("DOMNodeRemoved", removeFocus); + } + + if (amlNode.$isWindowContainer > -2) { + amlNode.removeEventListener("focus", trackChildFocus); + amlNode.removeEventListener("blur", trackChildFocus); + } + }; + + var focusLoopDetect; + this.$focus = function(amlNode, e, force){ + var aEl = this.document.activeElement; + if (aEl == amlNode && !force) + return; //or maybe when force do $focus + + + + this.$settingFocus = amlNode; + + if (!e) + e = {}; + + e.toElement = amlNode; + e.fromElement = aEl; + + if (aEl && aEl != amlNode && focusLoopDetect != aEl) { + focusLoopDetect = aEl; + + aEl.blur(true, e); + + + + if (focusLoopDetect != aEl) + return false; + } + + (apf.activeElement = this.document.activeElement = this.document.documentElement.$lastFocussed = amlNode).focus(true, e); + + this.$settingFocus = null; + + apf.dispatchEvent("movefocus", { + toElement : amlNode + }); + + + + + + + }; + + this.$blur = function(amlNode){ + var aEl = this.document.activeElement; + if (aEl != amlNode) + return false; + + + + aEl.$focusParent.$lastFocussed = null; + apf.activeElement = this.document.activeElement = null; + + apf.dispatchEvent("movefocus", { + fromElement : amlNode + }); + + + }; + + var lastFocusParent; + + this.$focusDefault = function(amlNode, e){ + var fParent = findFocusParent(amlNode); + this.$focusLast(fParent, e); + }; + + this.$focusRoot = function(e){ + var docEl = apf.document.documentElement; + if (this.$focusLast(docEl, e) === false) { + //docEl.$lastFocussed = null; + //this.moveNext(null, apf.document.documentElement, true, e); + } + }; + + this.$focusLast = function(amlNode, e, ignoreVisible){ + var lf = amlNode.$lastFocussed; + + if (lf && lf.parentNode && lf.$focussable === true + && (ignoreVisible || lf.$ext.offsetHeight)) { + this.$focus(lf, e, true); + } + else { //Let's find the object to focus first + var next, node = amlNode, skip; + while (node) { + if (!skip && node.focussable !== false && node.$focussable === true && !node.$tabList + && (ignoreVisible || node.$ext && node.$ext.offsetHeight) && node.disabled < 1) { + this.$focus(node, e, true); + break; + } + + //Walk sub tree + if ((next = !skip && node.firstChild || !(skip = false) && node.nextSibling)) { + node = next; + if (node.$isWindowContainer > 0) + skip = true; + } + else if (node == amlNode) { + if (node.$isWindowContainer) + this.$focus(node, e, true); + return; + } + else { + do { + node = node.parentNode; + } while (node && !node.nextSibling && node != amlNode + && !node.$isWindowContainer) + + if (node == amlNode) { + if (node.$isWindowContainer) + this.$focus(node, e, true); + return; //do nothing + } + + if (node) { + if (node.$isWindowContainer) { + this.$focus(node, e, true); + break; + } + + node = node.nextSibling; + } + } + } + + if (!node) + this.$focus(apf.document.documentElement);//return false;// + + /*@todo get this back from SVN + var node, list = amlNode.$tabList; + for (var i = 0; i < list.length; i++) { + node = list[i]; + if (node.focussable !== false && node.$focussable === true + && (ignoreVisible || node.$ext.offsetHeight)) { + this.$focus(node, e, true); + return; + } + } + + this.$focus(apf.document.documentElement);*/ + } + }; + + function trackChildFocus(e){ + if (e.name == "blur") { + if (e.srcElement != this && this.$blur) + this.$blur(); + return; + } + + if (e.srcElement != this && this.$focus && (!e || !e.mouse || this.$focussable == apf.KEYBOARD_MOUSE)) + this.$focus(); + + if (e.srcElement == this || e.trackedChild) { + e.trackedChild = true; + return; + } + + this.$lastFocussed = e.srcElement; + + if (this.localName && this.localName.indexOf("window") > -1) + e.trackedChild = true; + } + + function findFocusParent(amlNode){ + var node = amlNode; + do { + node = node.parentNode; + } while(node && !node.$isWindowContainer); + //(!node.$focussable || node.focussable === false) + + return node || apf.document.documentElement; + } + + //Dom handler + //@todo make this look at the dom tree insertion point to determine tabindex + function moveFocus(e){ + if (e && e.currentTarget != this) + return; + + if (this.$isWindowContainer) + apf.window.$tabList.pushUnique(this); + else + apf.window.$addFocus(this, this.tabindex, true) + } + + //Dom handler + function removeFocus(e){ + if (e && (e.currentTarget != this || e.$doOnlyAdmin)) + return; + + //@todo apf3.0 this should be fixed by adding domremovenode events to all children + var list = this.$focusParent.$tabList; + var nodes = this.childNodes; + for (var i = 0, l = nodes.length; i < l; i++) { + list.remove(nodes[i]); //@todo assuming no windows here + } + + if (apf.document.activeElement == this) + apf.window.moveNext(); + + if (this.$isWindowContainer) { + apf.window.$tabList.remove(this); //@todo this can't be right + return; + } + + if (!this.$focusParent) + return; + + list.remove(this); + //this.$focusParent = null; //@experimental to not execute this + } + + /**** Focus API ****/ + + /** + * Determines whether a given aml element has the focus. + * @param {AMLElement} the element to check + * @returns {Boolean} whether the element has focus. + */ + this.hasFocus = function(amlNode){ + return this.document.activeElement == amlNode; + }; + + /** + * @private + */ + this.moveNext = function(shiftKey, relObject, switchWindows, e){ + if (switchWindows && apf.document.activeElement) { + var p = apf.document.activeElement.$focusParent; + if (p.visible && p.modal) + return false; + } + + var dir, start, next, + amlNode = relObject || apf.document.activeElement, + fParent = amlNode + ? (switchWindows && amlNode.$isWindowContainer + && amlNode.$isWindowContainer != -1 + ? apf.window + : e && e.innerList ? amlNode.$focusParent : amlNode.$focusParent2 || amlNode.$focusParent) + : apf.document.documentElement, + list = fParent.$tabList; + + if (amlNode && (switchWindows || amlNode != apf.document.documentElement)) { + start = (list || []).indexOf(amlNode); + if (start == -1) { + + + return; + } + } + else { + start = -1; + } + + if (this.document.activeElement && this.document.activeElement == amlNode + && list.length == 1 || list.length == 0) + return false; + + dir = (shiftKey ? -1 : 1); + next = start; + if (start < 0) + start = 0; + do { + next += dir; + + if (next >= list.length) + next = 0; + else if (next < 0) + next = list.length - 1; + + if (start == next && amlNode) { + if (list[0].$isWindowContainer) + this.$focus(list[0], e); + + return false; //No visible enabled element was found + } + + amlNode = list[next]; + } + while (!amlNode + || amlNode.disabled > 0 + || amlNode == apf.document.activeElement + || (switchWindows ? !amlNode.visible : amlNode.$ext && !amlNode.$ext.offsetHeight) + || amlNode.focussable === false + || switchWindows && !amlNode.$tabList.length); + + if (fParent == apf.window && amlNode.$isWindowContainer != -2) { + this.$focusLast(amlNode, {mouse:true}, switchWindows); + } + else { + (e || (e = {})).shiftKey = shiftKey; + this.$focus(amlNode, e); + } + + + }; + + /** + * @private + */ + this.focusDefault = function(){ + + + if (this.moveNext() === false) + this.moveNext(null, apf.document.documentElement, true) + }; + + + + /**** Set Window Events ****/ + + apf.addListener(window, "beforeunload", function(){ + return apf.dispatchEvent("exit"); + }); + + //@todo apf3.x why is this loaded twice + apf.addListener(window, "unload", function(){ + if (!apf) + return; + + apf.window.isExiting = true; + apf.window.destroy(); + }); + + + + var timer, state = "", last = ""; + this.$focusfix = function(){ + + state += "a"; + clearTimeout(timer); + $setTimeout("window.focus();"); + timer = $setTimeout(determineAction); + }; + + this.$focusfix2 = function(){ + + state += "b"; + clearTimeout(timer); + timer = $setTimeout(determineAction); + }; + + this.$blurfix = function(){ + + state += "c"; + clearTimeout(timer); + timer = $setTimeout(determineAction); + }; + + function determineAction(){ + clearTimeout(timer); + + //apf.console.info(state); + if (state == "e" || state == "c" + || state.charAt(0) == "x" && !state.match(/eb$/) + || state == "ce" || state == "de") { //|| state == "ae" + if (last != "blur") { + last = "blur"; + apf.window.dispatchEvent("blur"); + //apf.console.warn("blur"); + } + } + else { + if (last != "focus") { + last = "focus"; + apf.window.dispatchEvent("focus"); + //apf.console.warn("focus"); + } + } + + state = ""; + timer = null; + } + + apf.addListener(window, "focus", this.$focusevent = function(){ + + if (apf.hasFocusBug) { + state += "d"; + clearTimeout(timer); + timer = $setTimeout(determineAction); + } + else { + clearTimeout(iframeFixTimer) + iframeFix.newState = "focus"; + //apf.console.warn("win-focus"); + iframeFixTimer = $setTimeout(iframeFix, 10); + } + }); + + apf.addListener(window, "blur", this.$blurevent = function(){ + if (!apf) return; + + + if (apf.hasFocusBug) { + state += "e"; + clearTimeout(timer); + timer = $setTimeout(determineAction); + } + else { + clearTimeout(iframeFixTimer) + iframeFix.newState = "blur"; + //apf.console.warn("win-blur"); + iframeFixTimer = $setTimeout(iframeFix, 10); + } + }); + + var iframeFixTimer; + function iframeFix(){ + clearTimeout(iframeFixTimer); + + var newState = iframeFix.newState; + if (last == newState) + return; + + last = newState; + + apf.dispatchEvent(last); + //apf.console.warn(last); + } + + this.hasFocus = function(){ + return (last == "focus"); + }; + + + + /**** Keyboard and Focus Handling ****/ + + apf.addListener(document, "contextmenu", function(e){ + if (!e) + e = event; + + + var pos, ev, + amlNode = apf.findHost(e.srcElement || e.target) + || apf.document.activeElement + || apf.document && apf.document.documentElement; + + if (amlNode && amlNode.localName == "menu") //The menu is already visible + return false; + + + //if (amlNode && amlNode.localName == "menu") + //amlNode = amlNode.parentNode; + + if (apf.contextMenuKeyboard) { + if (amlNode) { + pos = amlNode.selected + ? apf.getAbsolutePosition(amlNode.$selected) + : apf.getAbsolutePosition(amlNode.$ext || amlNode.$pHtmlNode); + } + else { + pos = [0, 0]; + } + + ev = { + x : pos[0] + 10 + document.documentElement.scrollLeft, + y : pos[1] + 10 + document.documentElement.scrollTop, + amlNode : amlNode, + htmlEvent : e + } + } + else { + if (e.htmlEvent) { + ev = e; + } + else { + ev = { //@todo probably have to deduct the border of the window + x : e.clientX + document.documentElement.scrollLeft, + y : e.clientY + document.documentElement.scrollTop, + htmlEvent : e + } + } + } + + ev.bubbles = true; //@todo discuss this, are we ok with bubbling? + + apf.contextMenuKeyboard = null; + + if ((amlNode || apf).dispatchEvent("contextmenu", ev) === false + || ev.returnValue === false) { + if (e.preventDefault) + e.preventDefault(); + return false; + } + + + if (apf.config.disableRightClick) { + if (e.preventDefault) + e.preventDefault(); + return false; + } + }); + + apf.addListener(document, "mouseup", function(e){ + if (!e) e = event; + + apf.dispatchEvent("mouseup", { + htmlEvent : e + }); + }); + + var ta = {"INPUT":1, "TEXTAREA":1, "SELECT":1, "EMBED":1, "OBJECT":1}; + apf.addListener(document, "mousedown", this.$mousedown = function(e){ + + if (!e) e = event; + var p, + amlNode = apf.findHost(e.srcElement || e.target); + /*cEditable = amlNode && amlNode.liveedit + + ;*/ + + if (apf.popup.last && (!amlNode || apf.popup.last != amlNode.$uniqueId) + && apf.popup.cache[apf.popup.last] + && !apf.isChildOf(apf.popup.cache[apf.popup.last].content, e.srcElement || e.target, true)) + apf.popup.forceHide(); + + + if (amlNode === false) + amlNode = apf.document.activeElement; + + + //Make sure the user cannot leave a modal window + if ((!amlNode || ((!amlNode.$focussable || amlNode.focussable === false) + && amlNode.canHaveChildren != 2 && !amlNode.$focusParent)) + && apf.config.allowBlur) { + lastFocusParent = null; + if (apf.document.activeElement) + apf.document.activeElement.blur(); + } + else if (amlNode) { //@todo check this for documentElement apf3.0 + if ((p = apf.document.activeElement + && apf.document.activeElement.$focusParent || lastFocusParent) + && p.visible && p.modal && amlNode.$focusParent != p + && amlNode.$isWindowContainer != -1) { + apf.window.$focusLast(p, {mouse: true, ctrlKey: e.ctrlKey}); + } + else if (!amlNode && apf.document.activeElement) { + apf.window.$focusRoot(); + } + else if (amlNode.$isWindowContainer == -1) { + if (amlNode.$tabList.length) + apf.window.moveNext(null, amlNode.$tabList[0], null, {mouse: true, innerList: true}); + else + apf.window.$focus(amlNode); + } + else if ((amlNode.disabled == undefined || amlNode.disabled < 1) + && amlNode.focussable !== false) { + if (amlNode.$focussable) { // === apf.KEYBOARD_MOUSE + apf.window.$focus(amlNode, {mouse: true, ctrlKey: e.ctrlKey}); + } + else if (amlNode.canHaveChildren == 2) { + if (!apf.config.allowBlur || !apf.document.activeElement + || apf.document.activeElement.$focusParent != amlNode) + apf.window.$focusLast(amlNode, {mouse: true, ctrlKey: e.ctrlKey}); + } + else { + if (!apf.config.allowBlur || amlNode != apf.document.documentElement) + apf.window.$focusDefault(amlNode, {mouse: true, ctrlKey: e.ctrlKey}); + } + } + else { + apf.window.$focusDefault(amlNode, {mouse: true, ctrlKey: e.ctrlKey}); + } + + + if (apf.hasFocusBug) { + var isTextInput = (ta[e.srcElement.tagName] + || e.srcElement.isContentEditable) && !e.srcElement.disabled + || amlNode.$isTextInput + && amlNode.$isTextInput(e) && amlNode.disabled < 1; + + if (!amlNode || !isTextInput) + apf.window.$focusfix(); + } + else if (!last) { + apf.window.$focusevent(); + } + + } + + + apf.dispatchEvent("mousedown", { + htmlEvent : e, + amlNode : amlNode || apf.document.documentElement + }); + + //Non IE/ iPhone selection handling + if (apf.isIE || apf.isIphone) + return; + + var canSelect = !((!apf.document + && (!apf.isParsingPartial || amlNode) + || apf.dragMode) && !ta[e.target && e.target.tagName]); + + if (canSelect && amlNode) { + if (!e.target && e.srcElement) + e.target = {}; + var isTextInput = (ta[e.target.tagName] + || e.target.contentEditable == "true") && !e.target.disabled //@todo apf3.0 need to loop here? + || amlNode.$isTextInput + && amlNode.$isTextInput(e) && amlNode.disabled < 1; + + //(!amlNode.canHaveChildren || !apf.isChildOf(amlNode.$int, e.srcElement)) + if (!apf.config.allowSelect && !isTextInput + && amlNode.nodeType != amlNode.NODE_PROCESSING_INSTRUCTION + && !amlNode.textselect) //&& (!amlNode.$int || amlNode.$focussable) //getElementsByTagNameNS(apf.ns.xhtml, "*").length + canSelect = false; + } + + if (!canSelect && e.button != 2) { // && !cEditable + if (e.preventDefault) + e.preventDefault(); + + try{ + if (document.activeElement && document.activeElement.contentEditable == "true") //@todo apf3.0 need to loop here? + document.activeElement.blur(); + }catch(e){} + } + }); + + //IE selection handling + apf.addListener(document, "selectstart", function(e){ + if (!e) e = event; + + var amlNode = apf.findHost(e.srcElement); + var canSelect = !(!apf.document + && (!apf.isParsingPartial || amlNode) + || apf.dragMode); + + if (canSelect) { + //(!amlNode.canHaveChildren || !apf.isChildOf(amlNode.$int, e.srcElement)) + if (!apf.config.allowSelect + && (amlNode && amlNode.nodeType != amlNode.NODE_PROCESSING_INSTRUCTION + && !amlNode.textselect)) //&& !amlNode.$int // getElementsByTagNameNS(apf.ns.xhtml, "*").length + canSelect = false; + } + + if (!canSelect) { + e.returnValue = false; + return false; + } + }); + + // Keyboard forwarding to focussed object + apf.addListener(document, "keyup", this.$keyup = function(e){ + if (!e) e = event; + + + var ev = { + keyCode : e.keyCode, + ctrlKey : e.ctrlKey, + shiftKey : e.shiftKey, + altKey : e.altkey, + htmlEvent: e, + bubbles : true //@todo is this much slower? + }; + + var aEl = apf.document && apf.document.activeElement; + if ((aEl && !aEl.disableKeyboard + ? aEl.dispatchEvent("keyup", ev) + : apf.dispatchEvent("keyup", ev)) === false) { + apf.preventDefault(e); + return false; + } + + }); + + + var wheel = this.$mousewheel = function wheel(e) { + if (!e) + e = event; + + var delta = null; + if (e.wheelDelta) { + delta = e.wheelDelta / 120; + if (apf.isOpera) + delta *= -1; + } + else if (e.detail) { + delta = -e.detail / 3; + } + + if (delta !== null) { + //Fix for scrolling too much + if (apf.isIE) { + var el = e.srcElement || e.target; + while (el && el.scrollHeight <= el.offsetHeight) + el = el.parentNode || el.$parentNode; + + if (el && el.nodeType == 9) + el = el.documentElement; + + if (!el || el.nodeType != 1) return; + + if (el && el.tagName == "BODY" && "auto|scroll".indexOf(apf.getStyle(el, "overflowY")) == -1) + el = document.documentElement; + + if (el && "auto|scroll".indexOf(apf.getStyle(el, "overflowY")) > -1) { + var max, dist = 0.35 * el.offsetHeight * delta; + if (delta < 0) { + if (el && el.scrollTop >= (max = el.scrollHeight - el.offsetHeight + apf.getVerBorders(el)) + dist) { + el.scrollTop = max; + e.returnValue = false; + } + } + else { + if (el && el.scrollTop <= dist) { + el.scrollTop = 0; + e.returnValue = false; + } + } + } + } + + var ev = { + delta : delta, + target : e.target || e.srcElement, + button : e.button, + ctrlKey : e.ctrlKey, + shiftKey : e.shiftKey, + altKey : e.altKey, + bubbles : true, + htmlEvent : e + }; + + var amlNode = apf.findHost(e.srcElement || e.target); + var res = (amlNode || apf).dispatchEvent("mousescroll", ev); + if (res === false || ev.returnValue === false) { + if (e.preventDefault) + e.preventDefault(); + + e.returnValue = false; + } + } + } + + if (document.addEventListener) + document.addEventListener('DOMMouseScroll', wheel, false); + + window.onmousewheel = + document.onmousewheel = wheel; //@todo 2 keer events?? + + + //var browserNavKeys = {32:1,33:1,34:1,35:1,36:1,37:1,38:1,39:1,40:1} + + apf.addListener(document, "keyup", function(e){ + e = e || event; + + if (e.ctrlKey && e.keyCode == 9 && apf.document.activeElement) { + var w = apf.document.activeElement.$focusParent; + if (w.modal) { + if (e.preventDefault) + e.preventDefault(); + return false; + } + + apf.window.moveNext(e.shiftKey, + apf.document.activeElement.$focusParent, true); + + w = apf.document.activeElement.$focusParent; + if (w && w.bringToFront) + w.bringToFront(); + + if (e.preventDefault) + e.preventDefault(); + return false; + } + }); + + //@todo optimize this function + apf.addListener(document, "keydown", this.$keydown = function(e){ + e = e || event; + + + + + if (e.keyCode == 93) + apf.contextMenuKeyboard = true; + + + var amlNode = apf.document.activeElement, //apf.findHost(e.srcElement || e.target), + htmlNode = (e.explicitOriginalTarget || e.srcElement || e.target), + isTextInput = (ta[htmlNode.tagName] + || htmlNode.contentEditable || htmlNode.contentEditable == "true") //@todo apf3.0 need to loop here? + && !htmlNode.disabled + || amlNode && amlNode.$isTextInput + && amlNode.$isTextInput(e) && amlNode.disabled < 1; + + + //@todo move this to appsettings and use with_hotkey + var o, + ctrlKey = apf.isMac ? e.metaKey : e.ctrlKey; + if (!isTextInput && apf.config.undokeys && ctrlKey) { + //Ctrl-Z - Undo + if (e.keyCode == 90) { + o = apf.document.activeElement; + while (o && !o.getActionTracker && !o.$at) + o = o.parentNode; + if (!o) o = apf.window; + (o.$at || o.getActionTracker()).undo(); + } + //Ctrl-Y - Redo + else if (e.keyCode == 89) { + o = apf.document.activeElement; + while (o && !o.getActionTracker && !o.$at) + o = o.parentNode; + if (!o) o = apf.window; + (o.$at || o.getActionTracker()).redo(); + } + } + + + var eInfo = { + ctrlKey : e.ctrlKey, + metaKey : e.metaKey, + shiftKey : e.shiftKey, + altKey : e.altKey, + keyCode : e.keyCode, + htmlEvent : e, + isTextInput: isTextInput, + bubbles : true + }; + + delete eInfo.currentTarget; + + //Keyboard forwarding to focussed object + var aEl = amlNode; //isTextInput ? amlNode : + if ((aEl && !aEl.disableKeyboard && !aEl.editable + ? aEl.dispatchEvent("keydown", eInfo) + : apf.dispatchEvent("keydown", eInfo)) === false) { + apf.stopEvent(e); + if (apf.canDisableKeyCodes) { + try { + e.keyCode = 0; + } + catch(e) {} + } + return false; + } + + //Focus handling + else if ((!apf.config.disableTabbing || apf.document.activeElement) && e.keyCode == 9) { + //Window focus handling + if (e.ctrlKey && apf.document.activeElement) { + var w = apf.document.activeElement.$focusParent; + if (w.modal) { + if (e.preventDefault) + e.preventDefault(); + return false; + } + + apf.window.moveNext(e.shiftKey, + apf.document.activeElement.$focusParent, true); + + w = apf.document.activeElement.$focusParent; + if (w && w.bringToFront) + w.bringToFront(); + } + //Element focus handling + else if(!apf.document.activeElement || apf.document.activeElement.tagName != "menu") { + apf.window.moveNext(e.shiftKey); + } + + if (e.preventDefault) + e.preventDefault(); + return false; + } + + + //Disable backspace behaviour triggering the backbutton behaviour + var altKey = apf.isMac ? e.metaKey : e.altKey; + if (apf.config.disableBackspace + && e.keyCode == 8// || (altKey && (e.keyCode == 37 || e.keyCode == 39))) + && !isTextInput) { + if (apf.canDisableKeyCodes) { + try { + e.keyCode = 0; + } + catch(e) {} + } + e.returnValue = false; + } + + //Disable space behaviour of scrolling down the page + /*if(Application.disableSpace && e.keyCode == 32 && e.srcElement.tagName.toLowerCase() != "input"){ + e.keyCode = 0; + e.returnValue = false; + }*/ + + //Disable F5 refresh behaviour + if (apf.config.disableF5 && (e.keyCode == 116 || e.keyCode == 117)) { + if (apf.canDisableKeyCodes) { + try { + e.keyCode = 0; + } + catch(e) {} + } + else { + e.preventDefault(); + e.stopPropagation(); + } + //return false; + } + + + /*if (browserNavKeys[e.keyCode] && apf.document.activeElement + && apf.config.autoDisableNavKeys) + e.returnValue = false;*/ + + if (e.keyCode == 27) + e.returnValue = false; + + if (!apf.config.allowSelect + && e.shiftKey && (e.keyCode > 32 && e.keyCode < 41) + && !isTextInput) { + e.returnValue = false; + } + + //apf.dispatchEvent("keydown", null, eInfo); + + if (e.returnValue === false && e.preventDefault) + e.preventDefault(); + + return e.returnValue; + + }); + + apf.document = {}; + this.init = function(strAml){ + + if (apf.actiontracker) { + this.$at = new apf.actiontracker(); + this.$at.name = "default"; + + apf.nameserver.register("actiontracker", "default", this.$at); + + } + + + + + + + + //Put this in callback in between the two phases + + /*XForms and lazy devs support + if (!nodes.length && !apf.skins.skins["default"] && apf.autoLoadSkin) { + apf.console.warn("No skin file found, attempting to autoload the \ + default skin file: skins.xml"); + apf.loadAmlInclude(null, doSync, "skins.xml", true); + }*/ + + + this.$domParser = new apf.DOMParser(); + this.document = apf.document = this.$domParser.parseFromString(strAml, + "text/xml", { + + timeout : apf.config.initdelay, + + callback : function(doc){ + //@todo apf3.0 + + //Call the onload event (prevent recursion) + if (apf.parsed != 2) { + //@todo apf3.0 onload is being called too often + var inital = apf.parsed; + apf.parsed = 2; + apf.dispatchEvent("parse", { //@todo apf3.0 document + initial : inital + }); + apf.parsed = true; + } + + if (!apf.loaded) { + + + + //Set the default selected element + if (!apf.document.activeElement && (!apf.config.allowBlur + || apf.document.documentElement + && apf.document.documentElement.editable)) + apf.window.focusDefault(); + + + apf.loaded = true; + $setTimeout(function() { + apf.dispatchEvent("load"); + apf.addEventListener("$event.load", function(cb){ + cb(); + }); + }); + } + + //END OF ENTIRE APPLICATION STARTUP + + + + + } + }); //async + }; + + + var lastFocusElement; + this.addEventListener("focus", function(e){ + if (!apf.document.activeElement && lastFocusParent && !apf.isIphone) { + lastFocusElement.focus(); + /* + if (lastFocusParent.$isWindowContainer < 0) { + if (lastFocusParent.$tabList.length) + apf.window.moveNext(null, lastFocusParent.$tabList[0]); + else + apf.window.$focus(lastFocusParent); + } + else + apf.window.$focusLast(lastFocusParent);*/ + } + }); + this.addEventListener("blur", function(e){ + if (!apf.document.activeElement || apf.isIphone) + return; + + apf.document.activeElement.blur(true, {srcElement: this});//, {cancelBubble: true} + lastFocusParent = apf.document.activeElement.$focusParent; + lastFocusElement = apf.document.activeElement; + apf.activeElement = apf.document.activeElement = null; + }); + this.getLastActiveElement = function(){ + return apf.activeElement || lastFocusElement; + } + + + /** + * @private + */ + this.destroy = function(){ + this.$at = null; + + apf.unload(this); + + apf = + this.win = + this.window = + this.document = null; + + //@todo this is not needed... maybe use apf.removeListener + window.onfocus = + window.onerror = + window.onunload = + window.onbeforeunload = + window.onbeforeprint = + window.onafterprint = + window.onmousewheel = + window.onblur = null; + + //@todo use apf.removeEvent + + document.oncontextmenu = + document.onmousedown = + document.onmousemove = + document.onmouseup = + document.onmousewheel = + document.onkeyup = + document.onkeydown = null + + if (document.body) { + document.body.onmousedown = + document.body.onmousemove = + document.body.onmouseup = null; + + document.body.innerHTML = ""; + } + }; +}; +apf.window.prototype = new apf.Class().$init(); +apf.window = new apf.window(); + + +/** + * @private + */ +apf.sanitizeTextbox = function(oTxt){ + if (!apf.hasFocusBug) + return; + + oTxt.onfocus = function(){ + if (apf.window) + apf.window.$focusfix2(); + }; + + oTxt.onblur = function(){ + if (apf.window) + apf.window.$blurfix(); + }; +}; + + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/core/browsers/gears.js)SIZE(1391)TIME(Fri, 14 Oct 2011 08:01:44 GMT)*/ + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/core/browsers/gecko.js)SIZE(6753)TIME(Fri, 14 Oct 2011 08:01:44 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + +/** + * Compatibility layer for Gecko based browsers. + * @private + */ +apf.runGecko = function(){ + if (apf.runNonIe) + apf.runNonIe(); + + /* *************************************************************************** + XSLT + ****************************************************************************/ + + + //XMLDocument.selectNodes + HTMLDocument.prototype.selectNodes = XMLDocument.prototype.selectNodes = function(sExpr, contextNode){ + try { + var oResult = this.evaluate(sExpr, (contextNode || this), + this.createNSResolver(this.documentElement), + 7, null); //XpathResult.ORDERED_NODE_ITERATOR_TYPE + } + catch(ex) { + var msg = ex.message; + if (ex.code == ex.INVALID_EXPRESSION_ERR) + msg = msg.replace(/the expression/i, "'" + sExpr + "'"); + throw new Error(ex.lineNumber, "XPath error: " + msg); + } + + var nodeList = new Array(oResult.snapshotLength); + nodeList.expr = sExpr; + for (var i = nodeList.length - 1; i >= 0; i--) + nodeList[i] = oResult.snapshotItem(i); + return nodeList; + }; + + //Element.selectNodes + Text.prototype.selectNodes = + Attr.prototype.selectNodes = + Element.prototype.selectNodes = function(sExpr){ + return this.ownerDocument.selectNodes(sExpr, this); + }; + + //XMLDocument.selectSingleNode + HTMLDocument.prototype.selectSingleNode = + XMLDocument.prototype.selectSingleNode = function(sExpr, contextNode){ + try { + var oResult = this.evaluate(sExpr, (contextNode || this), + this.createNSResolver(this.documentElement), + 9, null); //XpathResult.FIRST_ORDERED_NODE_TYPE + } + catch(ex) { + var msg = ex.message; + if (ex.code == ex.INVALID_EXPRESSION_ERR) + msg = msg.replace(/the expression/i, "'" + sExpr + "'"); + throw new Error(ex.lineNumber, "XPath error: " + msg); + } + + return oResult.singleNodeValue; + }; + + //Element.selectSingleNode + Text.prototype.selectSingleNode = + Attr.prototype.selectSingleNode = + Element.prototype.selectSingleNode = function(sExpr){ + return this.ownerDocument.selectSingleNode(sExpr, this); + }; + + + + var serializer = new XMLSerializer(); + var o = document.createElement("div"); + apf.insertHtmlNodes = function(nodeList, htmlNode, beforeNode, s) { + var frag, l, node, i; + if (nodeList) { + frag = document.createDocumentFragment(); + for (i = nodeList.length - 1; i >= 0; i--) { + node = nodeList[i]; + frag.insertBefore(node, frag.firstChild); + } + } + + o.innerHTML = typeof s == "string" ? s : apf.html_entity_decode(serializer.serializeToString(frag)) + .replace(/<([^>]+)\/>/g, "<$1>"); + + frag = document.createDocumentFragment(); + for (i = 0, l = o.childNodes.length; i < l; i++) { + node = o.childNodes[0]; + frag.appendChild(node); + } + + if (beforeNode) + htmlNode.insertBefore(frag, beforeNode); + htmlNode.appendChild(frag); + }; + + apf.insertHtmlNode = function(xmlNode, htmlNode, beforeNode, s) { + if (htmlNode.nodeType != 11 && !htmlNode.style) + return htmlNode.appendChild(xmlNode); + + if (!s) { + s = apf.html_entity_decode(xmlNode.serialize + ? xmlNode.serialize(true) + : ((xmlNode.nodeType == 3 || xmlNode.nodeType == 4 || xmlNode.nodeType == 2) + ? xmlNode.nodeValue + : serializer.serializeToString(xmlNode))); + } + + o.innerHTML = s.replace(/<([^>]+)\/>/g, "<$1>"); + + if (beforeNode) + htmlNode.insertBefore(o.firstChild, beforeNode); + else + htmlNode.appendChild(o.firstChild); + + return beforeNode ? beforeNode.previousSibling : htmlNode.lastChild; + }; + + /* ******** Error Compatibility ********************************************** + Error Object like IE + ****************************************************************************/ + function Error(nr, msg){ + + + this.message = msg; + this.nr = nr; + } + + apf.getHtmlLeft = function(oHtml){ + return (oHtml.offsetLeft + + (parseInt(apf.getStyle(oHtml.parentNode, "borderLeftWidth")) || 0)); + }; + + apf.getHtmlRight = function(oHtml){ + var p; + return (((p = oHtml.offsetParent).tagName == "BODY" + ? apf.getWindowWidth() + : p.offsetWidth) + - oHtml.offsetLeft - oHtml.offsetWidth + - (2 * (parseInt(apf.getStyle(p, "borderLeftWidth")) || 0)) + - (parseInt(apf.getStyle(p, "borderRightWidth")) || 0)); + }; + + apf.getHtmlTop = function(oHtml){ + return (oHtml.offsetTop + + (parseInt(apf.getStyle(oHtml.parentNode, "borderTopWidth")) || 0)); + }; + + apf.getHtmlBottom = function(oHtml){ + var p; + return (((p = oHtml.offsetParent).tagName == "BODY" + ? apf.getWindowHeight() + : p.offsetHeight) + - oHtml.offsetTop - oHtml.offsetHeight + - (2 * (parseInt(apf.getStyle(p, "borderTopWidth")) || 0)) + - (parseInt(apf.getStyle(p, "borderBottomWidth")) || 0)); + }; + + apf.getBorderOffset = function(oHtml){ + return [-1 * (parseInt(apf.getStyle(oHtml, "borderLeftWidth")) || 0), + -1 * (parseInt(apf.getStyle(oHtml, "borderTopWidth")) || 0)]; + }; +} + + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/core/browsers/ie.js)SIZE(14081)TIME(Fri, 14 Oct 2011 08:01:44 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + +/** + * Compatibility layer for Internet Explorer browsers. + * @private + */ +apf.runIE = function(){ + /* ******** XML Compatibility ************************************************ + Extensions to the xmldb + ****************************************************************************/ + var hasIE7Security = false, + hasIESecurity = false; + + + + + + apf.getHttpReq = hasIESecurity + ? function(){ + if (apf.availHTTP.length) + return apf.availHTTP.pop(); + + + + return new XMLHttpRequest(); + } + : function(){ + if (apf.availHTTP.length) + return apf.availHTTP.pop(); + + + + return new ActiveXObject("microsoft.XMLHTTP"); + }; + + apf.getXmlDom = hasIESecurity + ? function(message, noError){ + var xmlParser = getDOMParser(message, noError); + return xmlParser; + } + : function(message, noError, preserveWhiteSpaces){ + var xmlParser = new ActiveXObject("microsoft.XMLDOM"); + xmlParser.setProperty("SelectionLanguage", "XPath"); + if (preserveWhiteSpaces) + xmlParser.preserveWhiteSpace = true; + + if (message) { + if (apf.cantParseXmlDefinition) + message = message.replace(/\] \]/g, "] ]") + .replace(/^<\?[^>]*\?>/, "");//replace xml definition for IE5.0 + + xmlParser.loadXML(message); + + + if (xmlParser.parseError != 0 && apf.xmldb && apf.isJson(message)) { + try { + xmlParser = apf.json2Xml(message, noError); + } + catch(e) { + throw new Error(apf.formatErrorString(1051, null, + "JSON to XML conversion error occurred."+e.message, + "\nSource Text : " + message.replace(/\t/gi, " "))); + } + } + else + + if (!noError) + this.xmlParseError(xmlParser); + } + + return xmlParser; + }; + + apf.xmlParseError = function(xml){ + var xmlParseError = xml.parseError; + if (xmlParseError != 0) { + /* + http://msdn.microsoft.com/library/en-us/xmlsdk30/htm/xmobjpmexmldomparseerror.asp?frame=true + + errorCode Contains the error code of the last parse error. Read-only. + filepos Contains the absolute file position where the error occurred. Read-only. + line Specifies the line number that contains the error. Read-only. + linepos Contains the character position within the line where the error occurred. Read-only. + reason Explains the reason for the error. Read-only. + srcText Returns the full text of the line containing the error. Read-only. + url Contains the URL of the XML document containing the last error. Read-only. + */ + throw new Error(apf.formatErrorString(1050, null, + "XML Parse error on line " + xmlParseError.line, + xmlParseError.reason + "Source Text:\n" + + xmlParseError.srcText.replace(/\t/gi, " ") + )); + } + + return xml; + }; + + /** + * This method retrieves the current value of a property on a HTML element + * @param {HTMLElement} el the element to read the property from + * @param {String} prop the property to read + * @returns {String} + */ + apf.getStyle = function(el, prop) { + return el.currentStyle[prop]; + }; + + apf.insertHtmlNodes = function(nodeList, htmlNode, beforeNode, s){ + var str; + if (nodeList) { + for (str = [], i = 0, l = nodeList.length; i < l; i++) + str[i] = nodeList[i].xml; + } + str = s || apf.html_entity_decode(str.join("")); + + if (apf.isIE < 7) + str = str.replace(/style="background-image:([^"]*)"/g, + "find='$1' style='background-image:$1'"); + + try { + (beforeNode || htmlNode).insertAdjacentHTML(beforeNode + ? "beforebegin" + : "beforeend", str); + } + catch (e) { + //IE table hack + document.body.insertAdjacentHTML("beforeend", "" + + str + "
"); + + var x = document.body.lastChild.firstChild.firstChild; + for (i = x.childNodes.length - 1; i >= 0; i--) + htmlNode.appendChild(x.childNodes[apf.hasDynamicItemList ? 0 : i]); + } + + //Fix IE image loading bug + if (apf.isIE < 7) { + $setTimeout(function(){ + var nodes = htmlNode.getElementsByTagName("*"); + for (var s, i = 0, l = nodes.length; i < l; i++) { + if (s = nodes[i].getAttribute("find")) + nodes[i].style.backgroundImage = s.trim(); //@todo apf3.0 why is this needed? + } + }); + } + }; + + /* I have no idea what below code should do + + if (pNode.nodeType == 11) { + id = xmlNode.getAttribute("id"); + if (!id) + throw new Error(apf.formatErrorString(1049, null, "xmldb", "Inserting Cache Item in Document Fragment without an ID")); + + document.body.insertAdjacentHTML(beforeNode ? "beforebegin" : "beforeend", strHTML); + pNode.appendChild(document.getElementById(id)); + }*/ + apf.insertHtmlNode = function(xmlNode, htmlNode, beforeNode, str){ + if (htmlNode.nodeType != 11 && !htmlNode.style) + return htmlNode.appendChild(xmlNode); + + var pNode = beforeNode || htmlNode; + + if (!str) + str = apf.html_entity_decode(xmlNode.serialize + ? xmlNode.serialize(true) + : xmlNode.xml || xmlNode.outerHTML || xmlNode.nodeValue); + try { + pNode.insertAdjacentHTML(beforeNode + ? "beforeBegin" + : "beforeEnd", str); + } + catch(e) { + + + pNode.insertAdjacentHTML("afterEnd", str); + return pNode.nextSibling; + } + + if (beforeNode) + return beforeNode.previousSibling; + else + return htmlNode.lastChild.nodeType == 1 + ? htmlNode.lastChild + : htmlNode.lastChild.previousSibling; + + }; + + apf.getHtmlLeft = function(oHtml){ + return (oHtml.offsetLeft + - (apf.isIE > 7 && parseInt(oHtml.parentNode.currentStyle["borderLeftWidth"]) || 0)); + }; + + apf.getHtmlRight = function(oHtml){ + var p; + return (((p = oHtml.offsetParent).tagName == "BODY" + ? apf.getWindowWidth() + : p.offsetWidth) + - oHtml.offsetLeft - oHtml.offsetWidth + - (apf.isIE < 8 && parseInt(p.currentStyle["borderLeftWidth"]) || 0) + - (parseInt(p.currentStyle["borderRightWidth"]) || 0)); + }; + + apf.getHtmlTop = function(oHtml){ + return (oHtml.offsetTop + - (apf.isIE > 7 && parseInt(oHtml.offsetParent.currentStyle["borderTopWidth"]) || 0)); + }; + + apf.getHtmlBottom = function(oHtml){ + var p; + return (((p = oHtml.offsetParent).tagName == "BODY" + ? apf.getWindowHeight() + : p.offsetHeight) + - oHtml.offsetTop - oHtml.offsetHeight + - (apf.isIE < 8 && parseInt(p.currentStyle["borderTopWidth"]) || 0) + - (parseInt(p.currentStyle["borderBottomidth"]) || 0)); + }; + + apf.getBorderOffset = function(oHtml){ + return apf.isIE < 8 && [0,0] || [parseInt(oHtml.currentStyle["borderLeftWidth"]) || 0, + parseInt(oHtml.currentStyle["borderTopWidth"]) || 0] + }; + + apf.getOpacity = function(oHtml) { + return parseInt(((oHtml.currentStyle["filter"] || "").match(/alpha\(opacity=(\d*)\)/) || [0,0])[1]) / 100; + }; + + apf.setOpacity = function(oHtml, value){ + oHtml.style.filter = value == 1 + ? "" + : "alpha(opacity=" + Math.round(value * 100) + ")"; + }; + + + /** + * @private + */ + apf.popup2 = { + cache: {}, + setContent: function(cacheId, content, style, width, height){ + if (!this.popup) + this.init(); + + this.cache[cacheId] = { + content: content, + style : style, + width : width, + height : height + }; + if (content.parentNode) + content.parentNode.removeChild(content); + if (style) + apf.importCssString(style, this.popup.document); + + return this.popup.document; + }, + + removeContent: function(cacheId){ + this.cache[cacheId] = null; + delete this.cache[cacheId]; + }, + + init: function(){ + this.popup = window.createPopup(); + + this.popup.document.write('\ + \ + \ + \ + \ + \ + \ + '); + + var c = apf; + this.popup.document.body.onmousemove = function(){ + this.c = c + } + }, + + show: function(cacheId, x, y, animate, ref, width, height, callback){ + if (!this.popup) + this.init(); + var o = this.cache[cacheId]; + //if(this.last != cacheId) + this.popup.document.body.innerHTML = o.content.outerHTML; + + if (animate) { + var iVal, steps = 7, i = 0, popup = this.popup; + iVal = setInterval(function(){ + var value = ++i * ((height || o.height) / steps); + popup.show(x, y, width || o.width, value, ref); + popup.document.body.firstChild.style.marginTop + = (i - steps - 1) * ((height || o.height) / steps); + if (i > steps) { + clearInterval(iVal) + callback(popup.document.body.firstChild); + } + }, 10); + } + else { + this.popup.show(x, y, width || o.width, height || o.height, ref); + } + + this.last = cacheId; + }, + + hide: function(){ + if (this.popup) + this.popup.hide(); + }, + + forceHide: function(){ + if (this.last) + apf.lookup(this.last).dispatchEvent("popuphide"); + }, + + destroy: function(){ + if (!this.popup) + return; + this.popup.document.body.c = null; + this.popup.document.body.onmouseover = null; + } + }; + + + + apf.importClass(apf.runXpath, true, self); + +} + + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/core/browsers/iphone.js)SIZE(11827)TIME(Fri, 14 Oct 2011 08:01:44 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/core/browsers/non_ie.js)SIZE(24354)TIME(Tue, 06 Dec 2011 16:22:54 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + +/** + * @private + */ +apf.runNonIe = function (){ + + + DocumentFragment.prototype.getElementById = function(id){ + return this.childNodes.length ? this.childNodes[0].ownerDocument.getElementById(id) : null; + }; + + + + /**** XML Serialization ****/ + if (XMLDocument.prototype.__defineGetter__) { + //XMLDocument.xml + XMLDocument.prototype.__defineGetter__("xml", function(){ + return (new XMLSerializer()).serializeToString(this); + }); + XMLDocument.prototype.__defineSetter__("xml", function(){ + throw new Error(apf.formatErrorString(1042, null, "XML serializer", "Invalid assignment on read-only property 'xml'.")); + }); + + //Node.xml + Node.prototype.__defineGetter__("xml", function(){ + if (this.nodeType == 3 || this.nodeType == 4 || this.nodeType == 2) + return this.nodeValue; + return (new XMLSerializer()).serializeToString(this); + }); + + //Node.xml + Element.prototype.__defineGetter__("xml", function(){ + return (new XMLSerializer()).serializeToString(this); + }); + } + + /* ******** HTML Interfaces ************************************************** + insertAdjacentHTML(), insertAdjacentText() and insertAdjacentElement() + ****************************************************************************/ + if (typeof HTMLElement!="undefined") { + if (!HTMLElement.prototype.insertAdjacentElement) { + Text.prototype.insertAdjacentElement = + HTMLElement.prototype.insertAdjacentElement = function(where,parsedNode){ + switch (where.toLowerCase()) { + case "beforebegin": + this.parentNode.insertBefore(parsedNode,this); + break; + case "afterbegin": + this.insertBefore(parsedNode,this.firstChild); + break; + case "beforeend": + this.appendChild(parsedNode); + break; + case "afterend": + if (this.nextSibling) + this.parentNode.insertBefore(parsedNode,this.nextSibling); + else + this.parentNode.appendChild(parsedNode); + break; + } + }; + } + + if (!HTMLElement.prototype.insertAdjacentHTML) { + Text.prototype.insertAdjacentHTML = + HTMLElement.prototype.insertAdjacentHTML = function(where,htmlStr){ + var r = this.ownerDocument.createRange(); + r.setStartBefore(apf.isWebkit + ? document.body + : (self.document ? document.body : this)); + var parsedHTML = r.createContextualFragment(htmlStr); + this.insertAdjacentElement(where, parsedHTML); + }; + } + + if (!HTMLBodyElement.prototype.insertAdjacentHTML) //apf.isWebkit) + HTMLBodyElement.prototype.insertAdjacentHTML = HTMLElement.prototype.insertAdjacentHTML; + + if (!HTMLElement.prototype.insertAdjacentText) { + Text.prototype.insertAdjacentText = + HTMLElement.prototype.insertAdjacentText = function(where,txtStr){ + var parsedText = document.createTextNode(txtStr); + this.insertAdjacentElement(where,parsedText); + }; + } + + //HTMLElement.removeNode + HTMLElement.prototype.removeNode = function(){ + if (!this.parentNode) return; + + this.parentNode.removeChild(this); + }; + + //Currently only supported by Gecko + if (HTMLElement.prototype.__defineSetter__) { + //HTMLElement.innerText + HTMLElement.prototype.__defineSetter__("innerText", function(sText){ + var s = "" + sText; + this.innerHTML = s.replace(/\&/g, "&") + .replace(//g, ">"); + }); + + HTMLElement.prototype.__defineGetter__("innerText", function(){ + return this.innerHTML.replace(/<[^>]+>/g,"") + .replace(/\s\s+/g, " ").replace(/^\s+|\s+$/g, " "); + }); + + HTMLElement.prototype.__defineGetter__("outerHTML", function(){ + return (new XMLSerializer()).serializeToString(this); + }); + } + } + + /* ******** XML Compatibility ************************************************ + Giving the Mozilla XML Parser the same interface as IE's Parser + ****************************************************************************/ + var ASYNCNOTSUPPORTED = false; + + //Test if Async is supported + try { + XMLDocument.prototype.async = true; + ASYNCNOTSUPPORTED = true; + } catch(e) {/*trap*/} + + Document.prototype.onreadystatechange = null; + Document.prototype.parseError = 0; + + Array.prototype.item = function(i){return this[i];}; + Array.prototype.expr = ""; + + /*try{ + XMLDocument.prototype.readyState = 0; + }catch(e){}*/ + + XMLDocument.prototype.$clearDOM = function(){ + while (this.hasChildNodes()) + this.removeChild(this.firstChild); + }; + + XMLDocument.prototype.$copyDOM = function(oDoc){ + this.$clearDOM(); + + if (oDoc.nodeType == 9 || oDoc.nodeType == 11) { + var oNodes = oDoc.childNodes; + + for (var i = 0; i < oNodes.length; i++) + this.appendChild(this.importNode(oNodes[i], true)); + } + else if (oDoc.nodeType == 1) + this.appendChild(this.importNode(oDoc, true)); + }; + + //XMLDocument.loadXML(); + XMLDocument.prototype.loadXML = function(strXML){ + apf.xmldb.setReadyState(this, 1); + var sOldXML = this.xml || this.serialize(); + var oDoc = (new DOMParser()).parseFromString(strXML, "text/xml"); + apf.xmldb.setReadyState(this, 2); + this.$copyDOM(oDoc); + apf.xmldb.setReadyState(this, 3); + apf.xmldb.loadHandler(this); + return sOldXML; + }; + + Node.prototype.getElementById = function(id){}; + + HTMLElement.prototype.replaceNode = + Element.prototype.replaceNode = function(xmlNode){ + if (!this.parentNode) return; + + this.parentNode.insertBefore(xmlNode, this); + this.parentNode.removeChild(this); + }; + + //XMLDocument.load + XMLDocument.prototype.$load = XMLDocument.prototype.load; + XMLDocument.prototype.load = function(sURI){ + var oDoc = document.implementation.createDocument("", "", null); + oDoc.$copyDOM(this); + this.parseError = 0; + apf.xmldb.setReadyState(this, 1); + + try { + if (this.async == false && ASYNCNOTSUPPORTED) { + var tmp = new XMLHttpRequest(); + tmp.open("GET", sURI, false); + tmp.overrideMimeType("text/xml"); + tmp.send(null); + apf.xmldb.setReadyState(this, 2); + this.$copyDOM(tmp.responseXML); + apf.xmldb.setReadyState(this, 3); + } else + this.$load(sURI); + } + catch (objException) { + this.parseError = -1; + } + finally { + apf.xmldb.loadHandler(this); + } + + return oDoc; + }; + + + + + + /** + * This method retrieves the current value of a property on a HTML element + * @param {HTMLElement} el the element to read the property from + * @param {String} prop the property to read + * @returns {String} + */ + var getStyle = apf.getStyle = function(el, prop) { + try{ + return (window.getComputedStyle(el, "") || {})[prop] || ""; + }catch(e){} + }; + + //XMLDocument.setProperty + HTMLDocument.prototype.setProperty = + XMLDocument.prototype.setProperty = function(x,y){}; + + /* ******** XML Compatibility ************************************************ + Extensions to the xmldb + ****************************************************************************/ + apf.getHttpReq = function(){ + if (apf.availHTTP.length) + return apf.availHTTP.pop(); + return new XMLHttpRequest(); + }; + + apf.getXmlDom = function(message, noError, preserveWhiteSpaces){ + var xmlParser; + if (message) { + if (preserveWhiteSpaces === false) + message = message.replace(/>[\s\n\r]*<"); + + xmlParser = new DOMParser(); + xmlParser = xmlParser.parseFromString(message, "text/xml"); + + + //xmlParser.documentElement.tagName == "parsererror" + if (xmlParser.getElementsByTagName("parsererror").length && apf.xmldb + && apf.isJson(message)) { + try { + xmlParser = apf.json2Xml(message, noError); + } + catch(e) { + throw new Error(apf.formatErrorString(1051, null, + "JSON to XML conversion error occurred.", + "\nSource Text : " + message.replace(/\t/gi, " "))); + } + } + else + + if (!noError) + this.xmlParseError(xmlParser); + } + else { + xmlParser = document.implementation.createDocument("", "", null); + } + + return xmlParser; + }; + + apf.xmlParseError = function(xml){ + //if (xml.documentElement.tagName == "parsererror") { + if (xml.getElementsByTagName("parsererror").length) { + var nodeValue = xml.documentElement.firstChild.nodeValue; + + if (nodeValue != null) { + var str = nodeValue.split("\n"), + linenr = str[2].match(/\w+ (\d+)/)[1], + message = str[0].replace(/\w+ \w+ \w+: (.*)/, "$1"); + } else { + if(nodeValue = xml.documentElement.firstChild.getElementsByTagName('div')[0].firstChild.nodeValue) { + var linenr = nodeValue.match(/line\s(\d*)/)[1] || "N/A", + message = nodeValue.match(/column\s\d*:(.*)/)[1] || "N/A"; + } + else { + var linenr = "N/A", + message = "N/A"; + } + } + + var srcText = xml.documentElement.lastChild.firstChild,//.split("\n")[0]; + srcMsg = ""; + if(srcText && srcText.nodeValue) { + srcMsg = "\nSource Text : " + srcText.nodeValue.replace(/\t/gi, " ") + } + throw new Error(apf.formatErrorString(1050, null, + "XML Parse Error on line " + linenr, message + srcMsg)); + } + + return xml; + }; + + + apf.xmldb.setReadyState = function(oDoc, iReadyState) { + oDoc.readyState = iReadyState; + if (oDoc.onreadystatechange != null && typeof oDoc.onreadystatechange == "function") + oDoc.onreadystatechange(); + }; + + apf.xmldb.loadHandler = function(oDoc){ + if (!oDoc.documentElement || oDoc.documentElement.tagName == "parsererror") + oDoc.parseError = -1; + + apf.xmldb.setReadyState(oDoc, 4); + }; + + // + //Fix XML Data-Island Support Problem with Form Tag + apf.Init.add(function(){ + var i, nodes = document.getElementsByTagName("form"); + for (i = 0; i < nodes.length; i++) + nodes[i].removeNode(); + nodes = document.getElementsByTagName("xml"); + for(i = 0; i < nodes.length; i++) + nodes[i].removeNode(); + nodes = null; + }); + + /*window.onerror = function(message, filename, linenr){ + if(++ERROR_COUNT > MAXMSG) return; + filename = filename ? filename.match(/\/([^\/]*)$/)[1] : "[Mozilla Library]"; + new Error("---- APF Error ----\nProcess : Javascript code in '" + filename + "'\nLine : " + linenr + "\nMessage : " + message); + return false; + }*/ + + if (document.body) + document.body.focus = function(){}; + + + + if (!document.elementFromPoint) { + Document.prototype.elementFromPointRemove = function(el){ + if (!this.RegElements) return; + + this.RegElements.remove(el); + }; + + Document.prototype.elementFromPointAdd = function(el){ + if (!this.RegElements) + this.RegElements = []; + this.RegElements.push(el); + }; + + Document.prototype.elementFromPointReset = function(RegElements){ + //define globals + FoundValue = []; + FoundNode = null; + LastFoundAbs = document.documentElement; + }; + + Document.prototype.elementFromPoint = function(x, y){ + // Optimization, Keeping last found node makes it ignore all lower levels + // when there is no possibility of changing positions and zIndexes + /*if(self.FoundNode){ + var sx = getElementPosX(FoundNode); + var sy = getElementPosY(FoundNode); + var ex = sx + FoundNode.offsetWidth; var ey = sy + FoundNode.offsetHeight; + } + if(!self.FoundNode || !(x > sx && x < ex && y > sy && y < ey))*/ + document.elementFromPointReset(); + + // Optimization only looking at registered nodes + if (this.RegElements) { + var calc_z = -1, + i, calc, n, sx, sy, ex, ey, z + for (calc_z = -1, calc, i = 0; i < this.RegElements.length; i++) { + n = this.RegElements[i]; + if (getStyle(n, "display") == "none") continue; + + sx = getElementPosX(n); + sy = getElementPosY(n); + ex = sx + n.offsetWidth; + ey = sy + n.offsetHeight; + + if (x > sx && x < ex && y > sy && y < ey) { + z = getElementZindex(n); + if (z > calc_z) { //equal z-indexes not supported + calc = [n, x, y, sx, sy]; + calc_z = z; + } + } + } + + if (calc) { + efpi(calc[0], calc[1], calc[2], 0, FoundValue, calc[3], calc[4]); + if (!FoundNode) { + FoundNode = calc[0]; + LastFoundAbs = calc[0]; + FoundValue = [calc_z]; + } + } + } + + if (!this.RegElements || !this.RegElements.length) + efpi(document.body, x, y, 0, [], getElementPosX(document.body), + getElementPosY(document.body)); + + return FoundNode; + }; + + function efpi(from, x, y, CurIndex, CurValue, px, py){ + var StartValue = CurValue, + StartIndex = CurIndex, + //Loop through childNodes + nodes = from.childNodes, + n, i, z, sx, sy, ex, ey, isAbs, isHidden, inSpace; + for (n, i = 0; i < from.childNodes.length; i++) { + n = from.childNodes[i]; + if (n.nodeType == 1 && getStyle(n, "display") != "none" && n.offsetParent) { + sx = px + n.offsetLeft - n.offsetParent.scrollLeft;//getElementPosX(n); + sy = py + n.offsetTop - n.offsetParent.scrollTop;//getElementPosY(n); + ex = sx + n.offsetWidth; + ey = sy + n.offsetHeight; + + //if(Child is position absolute/relative and overflow == "hidden" && !inSpace) continue; + isAbs = getStyle(n, "position"); + isAbs = (isAbs == "absolute") || (isAbs == "relative"); + isHidden = getStyle(n, "overflow") == "hidden"; + inSpace = (x > sx && x < ex && y > sy && y < ey); + + if (isAbs && isHidden && !inSpace) continue; + + CurIndex = StartIndex; + CurValue = StartValue.copy(); + + //if (Child is position absolute/relative and has zIndex) or overflow == "hidden" + z = parseInt(getStyle(n, "zIndex")) || 0; + if (isAbs && (z || z == 0) || isHidden) { + //if(!is position absolute/relative) zIndex = 0 + if (!isAbs) z = 0; + + //if zIndex >= FoundValue[CurIndex] + if (z >= (FoundValue[CurIndex] || 0)) { + //if zIndex > CurValue[CurIndex]; + if (z > (CurValue[CurIndex] || 0)) { + //CurValue = StartValue.copy(); + + //set CurValue[CurIndex] = zIndex + CurValue[CurIndex] = z; + } + + CurIndex++; + + //if(inSpace && CurIndex >= FoundValue.length) + if (inSpace && CurIndex >= FoundValue.length) { + //Set FoundNode is currentNode + FoundNode = n; + //Set FoundValue is CurValue + FoundValue = CurValue;//.copy(); + + LastFoundAbs = n; + } + } + else + continue; //Ignore this treedepth + } + else if(inSpace && CurIndex >= FoundValue.length){ + //else if CurValue[CurIndex] continue; //Ignore this treedepth + //else if(CurValue[CurIndex]) continue; + + //Set FoundNode is currentNode + FoundNode = n; + //Set FoundValue is CurValue + FoundValue = CurValue;//.copy(); + } + + //loop through childnodes recursively + efpi(n, x, y, CurIndex, CurValue, isAbs ? sx : px, isAbs ? sy : py) + } + } + } + + function getElementPosY(myObj){ + return myObj.offsetTop + parseInt(apf.getStyle(myObj, "borderTopWidth")) + + (myObj.offsetParent ? getElementPosY(myObj.offsetParent) : 0); + } + + function getElementPosX(myObj){ + return myObj.offsetLeft + parseInt(apf.getStyle(myObj, "borderLeftWidth")) + + (myObj.offsetParent ? getElementPosX(myObj.offsetParent) : 0); + } + + function getElementZindex(myObj){ + //This is not quite sufficient and should be changed + var z = 0, n, p = myObj; + while (p && p.nodeType == 1) { + z = Math.max(z, parseInt(getStyle(p, "zIndex")) || -1); + p = p.parentNode; + } + return z; + } + } + + + + apf.getOpacity = function(oHtml) { + return apf.getStyle(oHtml, "opacity"); + }; + + apf.setOpacity = function(oHtml, value){ + oHtml.style.opacity = value; + }; +} + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/core/browsers/o3.js)SIZE(9017)TIME(Fri, 14 Oct 2011 08:01:44 GMT)*/ + + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/core/browsers/opera.js)SIZE(6583)TIME(Fri, 14 Oct 2011 08:01:44 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + +/** + * Compatibility layer for Opera browsers. + * @private + */ +apf.runOpera = function (){ + if (apf.runNonIe) + apf.runNonIe(); + + /* *************************************************************************** + XML Serialization + ****************************************************************************/ + //XMLDocument.xml + + //Node.xml + /*Node.prototype.serialize = function(){ + return (new XMLSerializer()).serializeToString(this); + } + //Node.xml + + Node.prototype.serialize = + XMLDocument.prototype.serialize = + Element.prototype.serialize = function(){ + return (new XMLSerializer()).serializeToString(this); + };*/ + + + + //XMLDocument.selectNodes + Document.prototype.selectNodes = + XMLDocument.prototype.selectNodes = + HTMLDocument.prototype.selectNodes = function(sExpr, contextNode){ + var oResult = this.evaluate(sExpr, (contextNode ? contextNode : this), + this.createNSResolver(this.documentElement), + XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null); + var nodeList = new Array(oResult.snapshotLength); + nodeList.expr = sExpr; + for (var i = 0; i < nodeList.length; i++) + nodeList[i] = oResult.snapshotItem(i); + return nodeList; + }; + + //Element.selectNodes + Text.prototype.selectNodes = + Attr.prototype.selectNodes = + Element.prototype.selectNodes = function(sExpr){ + var doc = this.ownerDocument; + if (!doc.selectSingleNode) { + doc.selectSingleNode = HTMLDocument.prototype.selectSingleNode; + doc.selectNodes = HTMLDocument.prototype.selectNodes; + } + + if (doc.selectNodes) + return doc.selectNodes(sExpr, this); + else { + throw new Error(apf.formatErrorString(1047, null, "XPath Selection", + "Method selectNodes is only supported by XML Nodes")); + } + }; + + //XMLDocument.selectSingleNode + Document.prototype.selectSingleNode = + XMLDocument.prototype.selectSingleNode = + HTMLDocument.prototype.selectSingleNode = function(sExpr, contextNode){ + var nodeList = this.selectNodes("(" + sExpr + ")[1]", contextNode ? contextNode : null); + return nodeList.length > 0 ? nodeList[0] : null; + }; + + //Element.selectSingleNode + Text.prototype.selectSingleNode = + Attr.prototype.selectSingleNode = + Element.prototype.selectSingleNode = function(sExpr){ + var doc = this.ownerDocument; + if (!doc.selectSingleNode) { + doc.selectSingleNode = HTMLDocument.prototype.selectSingleNode; + doc.selectNodes = HTMLDocument.prototype.selectNodes; + } + + if (doc.selectSingleNode) { + return doc.selectSingleNode(sExpr, this); + } + else { + throw new Error(apf.formatErrorString(1048, null, "XPath Selection", + "Method selectSingleNode is only supported by XML Nodes. \nInfo : " + e)); + } + }; + + + + var serializer = new XMLSerializer(); + apf.insertHtmlNodes = function(nodeList, htmlNode, beforeNode, s) { + var node, frag, i, l; + if (nodeList) { + frag = document.createDocumentFragment(); + i = 0; + l = nodeList.length; + for (; i < l; i++) { + if (!(node = nodeList[i])) continue; + frag.appendChild(node); + } + } + + (beforeNode || htmlNode).insertAdjacentHTML(beforeNode + ? "beforebegin" + : "beforeend", s || apf.html_entity_decode(serializer.serializeToString(frag)).replace(/<([^>]+)\/>/g, "<$1>")); + }; + + apf.insertHtmlNode = function(xmlNode, htmlNode, beforeNode, s) { + if (htmlNode.nodeType != 11 && !htmlNode.style) + return htmlNode.appendChild(xmlNode); + + if (!s) { + s = apf.html_entity_decode(xmlNode.serialize + ? xmlNode.serialize(true) + : ((xmlNode.nodeType == 3 || xmlNode.nodeType == 4 || xmlNode.nodeType == 2) + ? xmlNode.nodeValue + : serializer.serializeToString(xmlNode))); + } + + (beforeNode || htmlNode).insertAdjacentHTML(beforeNode + ? "beforebegin" + : "beforeend", s.replace(/<([^>]+)\/>/g, "<$1>")); + + return beforeNode ? beforeNode.previousSibling : htmlNode.lastChild; + }; + + apf.getHtmlLeft = function(oHtml){ + return (oHtml.offsetLeft + - (parseInt(apf.getStyle(oHtml.parentNode, "borderLeftWidth")) || 0)); + }; + + apf.getHtmlRight = function(oHtml){ + var p; + return (((p = oHtml.offsetParent).tagName == "BODY" + ? apf.getWindowWidth() + : p.offsetWidth) + - oHtml.offsetLeft - oHtml.offsetWidth + - (parseInt(apf.getStyle(p, "borderRightWidth")) || 0)); + }; + + apf.getHtmlTop = function(oHtml){ + return (oHtml.offsetTop + - (parseInt(apf.getStyle(oHtml.offsetParent, "borderTopWidth")) || 0)); + }; + + apf.getHtmlBottom = function(oHtml){ + var p; + return (((p = oHtml.offsetParent).tagName == "BODY" + ? apf.getWindowHeight() + : p.offsetHeight) + - oHtml.offsetTop - oHtml.offsetHeight + - (parseInt(apf.getStyle(p, "borderBottomWidth")) || 0)); + }; + + apf.getBorderOffset = function(oHtml){ + return [parseInt(apf.getStyle(oHtml, "borderLeftWidth")) || 0, + parseInt(apf.getStyle(oHtml, "borderTopWidth")) || 0] + }; +}; + + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/core/browsers/webkit.js)SIZE(8405)TIME(Fri, 14 Oct 2011 08:01:44 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + +/** + * Compatibility layer for Webkit based browsers. + * @private + */ +apf.runWebkit = function(){ + + + + if (XMLHttpRequest.prototype.sendAsBinary === undefined) { + /** + * Binary support for Chrome 7+ which implements [ECMA-262] typed arrays + * @see http://www.khronos.org/registry/typedarray/specs/latest/ + */ + if (window.ArrayBuffer) { + XMLHttpRequest.prototype.sendAsBinary = function(string) { + var bytes = Array.prototype.map.call(string, function(c) { + return c.charCodeAt(0) & 0xff; + }); + this.send(new Uint8Array(bytes).buffer); + }; + } + } + + + + + + + HTMLDocument.prototype.selectNodes = XMLDocument.prototype.selectNodes = function(sExpr, contextNode){ + if (sExpr.substr(0,2) == "//") + sExpr = "." + sExpr; + + try { + var oResult = this.evaluate(sExpr, (contextNode || this), + this.createNSResolver(this.documentElement), + 7, null);//XPathResult.ORDERED_NODE_SNAPSHOT_TYPE + } + catch(ex) { + try { + var oResult = this.evaluate("child::" + sExpr, (contextNode || this), + this.createNSResolver(this.documentElement), + 7, null);//XPathResult.ORDERED_NODE_SNAPSHOT_TYPE + } + catch(ex) { + throw new Error("XPath error: " + ex.message + "\nLine: " + ex.lineNumber + "\nExpression: '" + sExpr + "'"); + } + } + + var nodeList = new Array(oResult.snapshotLength); + nodeList.expr = sExpr; + for (var i = nodeList.length - 1; i >= 0; i--) + nodeList[i] = oResult.snapshotItem(i); + return nodeList; + }; + + //Element.selectNodes + Text.prototype.selectNodes = + Attr.prototype.selectNodes = + Element.prototype.selectNodes = function(sExpr){ + return this.ownerDocument.selectNodes(sExpr, this); + }; + + //XMLDocument.selectSingleNode + HTMLDocument.prototype.selectSingleNode = XMLDocument.prototype.selectSingleNode = function(sExpr, contextNode){ + var nodeList = this.selectNodes("(" + sExpr + ")[1]", contextNode ? contextNode : null); + return nodeList.length > 0 ? nodeList[0] : null; + }; + + //Element.selectSingleNode + Text.prototype.selectSingleNode = + Attr.prototype.selectSingleNode = + Element.prototype.selectSingleNode = function(sExpr){ + return this.ownerDocument.selectSingleNode(sExpr, this); + }; + + + + var serializer = new XMLSerializer(); + apf.insertHtmlNodes = function(nodeList, htmlNode, beforeNode, s) { + var node, frag, a, i, l; + if (nodeList) { + frag = document.createDocumentFragment(); + a = [], i = 0, l = nodeList.length; + for (; i < l; i++) { + if (!(node = nodeList[i])) continue; + frag.appendChild(node); + } + } + + (beforeNode || htmlNode).insertAdjacentHTML(beforeNode + ? "beforebegin" + : "beforeend", s || apf.html_entity_decode(serializer.serializeToString(frag)) + .replace(/<([^>]+)\/>/g, "<$1>")); + }; + + apf.insertHtmlNode = function(xmlNode, htmlNode, beforeNode, s) { + if (htmlNode.nodeType != 11 && !htmlNode.style) + return htmlNode.appendChild(xmlNode); + + if (!s) { + s = apf.html_entity_decode(xmlNode.serialize + ? xmlNode.serialize(true) + : ((xmlNode.nodeType == 3 || xmlNode.nodeType == 4 || xmlNode.nodeType == 2) + ? xmlNode.nodeValue + : serializer.serializeToString(xmlNode))); + } + + (beforeNode || htmlNode).insertAdjacentHTML(beforeNode + ? "beforebegin" + : "beforeend", s.match(/<(IMG|LINK|META|BR|HR|BASEFONT)[^\/>]*/i) ? s.replace(/<([^>]+)\/>/g, "<$1 />") : s.replace(/<([^>]+)\/>/g, "<$1>")); + + return beforeNode ? beforeNode.previousSibling : htmlNode.lastChild; + }; + + apf.getHtmlLeft = function(oHtml){ + return oHtml.offsetLeft; + }; + + apf.getHtmlRight = function(oHtml){ + var p; + return (((p = oHtml.offsetParent).tagName == "BODY" + ? apf.getWindowWidth() + : p.offsetWidth) + - oHtml.offsetLeft - oHtml.offsetWidth + - (parseInt(apf.getStyle(p, "borderLeftWidth")) || 0) + - (parseInt(apf.getStyle(p, "borderRightWidth")) || 0)); + }; + + apf.getHtmlTop = function(oHtml){ + return oHtml.offsetTop + }; + + apf.getHtmlBottom = function(oHtml){ + var p; + return (((p = oHtml.offsetParent).tagName == "BODY" + ? apf.getWindowHeight() + : p.offsetHeight) + - oHtml.offsetTop - oHtml.offsetHeight + - (parseInt(apf.getStyle(p, "borderTopWidth")) || 0) + - (parseInt(apf.getStyle(p, "borderBottomWidth")) || 0)); + }; + + apf.getBorderOffset = function(oHtml){ + return [0,0]; + }; + + if (apf.runNonIe) + apf.runNonIe(); +}; + + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/core/browsers/node/XMLHttpRequest.js)SIZE(6419)TIME(Fri, 14 Oct 2011 08:01:44 GMT)*/ + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/core/crypto/barrett.js)SIZE(2650)TIME(Fri, 14 Oct 2011 08:01:44 GMT)*/ + +/** + * Crypt.Barrett, a class for performing Barrett modular reduction computations in + * JavaScript. + * + * Requires BigInt.js. + * + * Copyright 2004-2005 David Shapiro. + * + * You may use, re-use, abuse, copy, and modify this code to your liking, but + * please keep this header. + * + * Thanks! + * + * @author Dave Shapiro + */ + + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/core/crypto/base64.js)SIZE(6758)TIME(Fri, 14 Oct 2011 08:01:44 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + + +apf.crypto.Base64 = (function() { + + var b64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="; + + // public method for encoding + function encode(data) { + var o1, o2, o3, h1, h2, h3, h4, bits, i = 0, ac = 0, enc = "", + tmp_arr = []; + + if (!data) + return data; + + data = apf.crypto.UTF8.encode(data + ""); + + do { // pack three octets into four hexets + o1 = data.charCodeAt(i++); + o2 = data.charCodeAt(i++); + o3 = data.charCodeAt(i++); + + bits = o1 << 16 | o2 << 8 | o3; + + h1 = bits >> 18 & 0x3f; + h2 = bits >> 12 & 0x3f; + h3 = bits >> 6 & 0x3f; + h4 = bits & 0x3f; + + // use hexets to index into b64, and append result to encoded string + tmp_arr[ac++] = b64.charAt(h1) + b64.charAt(h2) + b64.charAt(h3) + + b64.charAt(h4); + } + while (i < data.length); + + enc = tmp_arr.join(""); + + switch (data.length % 3) { + case 1: + enc = enc.slice(0, -2) + '=='; + break; + case 2: + enc = enc.slice(0, -1) + '='; + break; + } + + return enc; + } + + // public method for decoding + function decode(data) { + var o1, o2, o3, h1, h2, h3, h4, bits, i = 0, ac = 0, tmp_arr = []; + + if (!data) { + return data; + } + + data += ""; + + do { // unpack four hexets into three octets using index points in b64 + h1 = b64.indexOf(data.charAt(i++)); + h2 = b64.indexOf(data.charAt(i++)); + h3 = b64.indexOf(data.charAt(i++)); + h4 = b64.indexOf(data.charAt(i++)); + + bits = h1 << 18 | h2 << 12 | h3 << 6 | h4; + + o1 = bits>>16 & 0xff; + o2 = bits>>8 & 0xff; + o3 = bits & 0xff; + + if (h3 == 64) + tmp_arr[ac++] = String.fromCharCode(o1); + else if (h4 == 64) + tmp_arr[ac++] = String.fromCharCode(o1, o2); + else + tmp_arr[ac++] = String.fromCharCode(o1, o2, o3); + } + while (i < data.length); + + return apf.crypto.UTF8.decode(tmp_arr.join("")); + } + + return { + decode: decode, + encode: encode + }; + +})(); + +apf.crypto.UTF8 = { + // private method for UTF-8 encoding + encode : function (string) { + // Encodes an ISO-8859-1 string to UTF-8 + // + // version: 905.1217 + // discuss at: http://phpjs.org/functions/utf8_encode + // + original by: Webtoolkit.info (http://www.webtoolkit.info/) + // + improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net) + // + improved by: sowberry + // + tweaked by: Jack + // + bugfixed by: Onno Marsman + // + improved by: Yves Sucaet + // + bugfixed by: Onno Marsman + // * example 1: utf8_encode('Kevin van Zonneveld'); + // * returns 1: 'Kevin van Zonneveld' + string = (string + "").replace(/\r\n/g, "\n").replace(/\r/g, "\n"); + + var tmp_arr = [], + start = 0, + end = 0, + c1, enc; + + for (var n = 0, l = string.length; n < l; n++) { + c1 = string.charCodeAt(n); + enc = null; + + if (c1 < 128) { + end++; + } + else if ((c1 > 127) && (c1 < 2048)) { + enc = String.fromCharCode((c1 >> 6) | 192) + + String.fromCharCode((c1 & 63) | 128); + } + else { + enc = String.fromCharCode((c1 >> 12) | 224) + + String.fromCharCode(((c1 >> 6) & 63) | 128) + + String.fromCharCode((c1 & 63) | 128); + } + if (enc !== null) { + if (end > start) + tmp_arr.push(string.substring(start, end)); + tmp_arr.push(enc); + start = end = n + 1; + } + } + + if (end > start) + tmp_arr.push(string.substring(start, string.length)); + + return tmp_arr.join(""); + }, + + // private method for UTF-8 decoding + decode : function (str_data) { + // Converts a UTF-8 encoded string to ISO-8859-1 + // + // version: 905.3122 + // discuss at: http://phpjs.org/functions/utf8_decode + // + original by: Webtoolkit.info (http://www.webtoolkit.info/) + // + input by: Aman Gupta + // + improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net) + // + improved by: Norman "zEh" Fuchs + // + bugfixed by: hitwork + // + bugfixed by: Onno Marsman + // + input by: Brett Zamir (http://brett-zamir.me) + // + bugfixed by: Kevin van Zonneveld (http://kevin.vanzonneveld.net) + // * example 1: utf8_decode('Kevin van Zonneveld'); + // * returns 1: 'Kevin van Zonneveld' + var tmp_arr = [], i = 0, ac = 0, c1 = 0, c2 = 0, c3 = 0; + + str_data += ""; + + while (i < str_data.length) { + c1 = str_data.charCodeAt(i); + if (c1 < 128) { + tmp_arr[ac++] = String.fromCharCode(c1); + i++; + } + else if ((c1 > 191) && (c1 < 224)) { + c2 = str_data.charCodeAt(i+1); + tmp_arr[ac++] = String.fromCharCode(((c1 & 31) << 6) | (c2 & 63)); + i += 2; + } + else { + c2 = str_data.charCodeAt(i+1); + c3 = str_data.charCodeAt(i+2); + tmp_arr[ac++] = String.fromCharCode(((c1 & 15) << 12) + | ((c2 & 63) << 6) | (c3 & 63)); + i += 3; + } + } + + return tmp_arr.join(''); + } + +}; + + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/core/crypto/bigint.js)SIZE(20439)TIME(Fri, 14 Oct 2011 08:01:44 GMT)*/ + +/** + * BigInt, a suite of routines for performing multiple-precision arithmetic in + * JavaScript. + * + * Copyright 1998-2005 David Shapiro. + * + * You may use, re-use, abuse, + * copy, and modify this code to your liking, but please keep this header. + * Thanks! + * + * @author Dave Shapiro + * @author Ian Bunning + * + * IMPORTANT THING: Be sure to set maxDigits according to your precision + * needs. Use the setMaxDigits() function to do this. See comments below. + * + * Tweaked by Ian Bunning + * Alterations: + * Fix bug in function biFromHex(s) to allow + * parsing of strings of length != 0 (mod 4) + * + * Changes made by Dave Shapiro as of 12/30/2004: + * + * The BigInt() constructor doesn't take a string anymore. If you want to + * create a BigInt from a string, use biFromDecimal() for base-10 + * representations, biFromHex() for base-16 representations, or + * biFromString() for base-2-to-36 representations. + * + * biFromArray() has been removed. Use biCopy() instead, passing a BigInt + * instead of an array. + * + * The BigInt() constructor now only constructs a zeroed-out array. + * Alternatively, if you pass , it won't construct any array. See the + * biCopy() method for an example of this. + * + * Be sure to set maxDigits depending on your precision needs. The default + * zeroed-out array ZERO_ARRAY is constructed inside the setMaxDigits() + * function. So use this function to set the variable. DON'T JUST SET THE + * VALUE. USE THE FUNCTION. + * + * ZERO_ARRAY exists to hopefully speed up construction of BigInts(). By + * precalculating the zero array, we can just use slice(0) to make copies of + * it. Presumably this calls faster native code, as opposed to setting the + * elements one at a time. I have not done any timing tests to verify this + * claim. + * Max number = 10^16 - 2 = 9999999999999998; + * 2^53 = 9007199254740992; + */ + + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/core/crypto/blowfish.js)SIZE(26046)TIME(Fri, 14 Oct 2011 08:01:44 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/core/crypto/md4.js)SIZE(9799)TIME(Fri, 14 Oct 2011 08:01:44 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/core/crypto/md5.js)SIZE(10997)TIME(Fri, 14 Oct 2011 08:01:44 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + + +apf.crypto.MD5 = { + /* + * Configurable variables. You may need to tweak these to be compatible with + * the server-side, but the defaults work in most cases. + */ + hexcase: 0, /* hex output format. 0 - lowercase; 1 - uppercase */ + b64pad : "", /* base-64 pad character. "=" for strict RFC compliance */ + chrsz : 8, /* bits per input character. 8 - ASCII; 16 - Unicode */ + + /** + * These are the functions you'll usually want to call + * They take string arguments and return either hex or base-64 encoded strings + * + * Example: + * var hash = apf.crypto.MD5.hex_md5("uzza"); //fddb7463a72e6b000abf631f558cf034 + */ + + hex_md5: function(s) { + return this.binl2hex(this.core_md5(this.str2binl(s), s.length * this.chrsz)); + }, + b64_md5: function(s) { + return this.binl2b64(this.core_md5(this.str2binl(s), s.length * this.chrsz)); + }, + str_md5: function(s) { + return this.binl2str(this.core_md5(this.str2binl(s), s.length * this.chrsz)); + }, + hex_hmac_md5: function(key, data) { + return this.binl2hex(this.core_hmac_md5(key, data)); + }, + b64_hmac_md5: function(key, data) { + return this.binl2b64(this.core_hmac_md5(key, data)); + }, + str_hmac_md5: function(key, data) { + return this.binl2str(this.core_hmac_md5(key, data)); + }, + /** + * Calculate the MD5 of an array of little-endian words, and a bit length + */ + core_md5: function(x, len) { + /* append padding */ + x[len >> 5] |= 0x80 << ((len) % 32); + x[(((len + 64) >>> 9) << 4) + 14] = len; + + var a = 1732584193, b = -271733879, c = -1732584194, d = 271733878; + + for(var i = 0; i < x.length; i += 16) { + var olda = a, oldb = b, oldc = c, oldd = d; + + a = this.md5_ff(a, b, c, d, x[i+ 0], 7 , -680876936); + d = this.md5_ff(d, a, b, c, x[i+ 1], 12, -389564586); + c = this.md5_ff(c, d, a, b, x[i+ 2], 17, 606105819); + b = this.md5_ff(b, c, d, a, x[i+ 3], 22, -1044525330); + a = this.md5_ff(a, b, c, d, x[i+ 4], 7 , -176418897); + d = this.md5_ff(d, a, b, c, x[i+ 5], 12, 1200080426); + c = this.md5_ff(c, d, a, b, x[i+ 6], 17, -1473231341); + b = this.md5_ff(b, c, d, a, x[i+ 7], 22, -45705983); + a = this.md5_ff(a, b, c, d, x[i+ 8], 7 , 1770035416); + d = this.md5_ff(d, a, b, c, x[i+ 9], 12, -1958414417); + c = this.md5_ff(c, d, a, b, x[i+10], 17, -42063); + b = this.md5_ff(b, c, d, a, x[i+11], 22, -1990404162); + a = this.md5_ff(a, b, c, d, x[i+12], 7 , 1804603682); + d = this.md5_ff(d, a, b, c, x[i+13], 12, -40341101); + c = this.md5_ff(c, d, a, b, x[i+14], 17, -1502002290); + b = this.md5_ff(b, c, d, a, x[i+15], 22, 1236535329); + + a = this.md5_gg(a, b, c, d, x[i+ 1], 5 , -165796510); + d = this.md5_gg(d, a, b, c, x[i+ 6], 9 , -1069501632); + c = this.md5_gg(c, d, a, b, x[i+11], 14, 643717713); + b = this.md5_gg(b, c, d, a, x[i+ 0], 20, -373897302); + a = this.md5_gg(a, b, c, d, x[i+ 5], 5 , -701558691); + d = this.md5_gg(d, a, b, c, x[i+10], 9 , 38016083); + c = this.md5_gg(c, d, a, b, x[i+15], 14, -660478335); + b = this.md5_gg(b, c, d, a, x[i+ 4], 20, -405537848); + a = this.md5_gg(a, b, c, d, x[i+ 9], 5 , 568446438); + d = this.md5_gg(d, a, b, c, x[i+14], 9 , -1019803690); + c = this.md5_gg(c, d, a, b, x[i+ 3], 14, -187363961); + b = this.md5_gg(b, c, d, a, x[i+ 8], 20, 1163531501); + a = this.md5_gg(a, b, c, d, x[i+13], 5 , -1444681467); + d = this.md5_gg(d, a, b, c, x[i+ 2], 9 , -51403784); + c = this.md5_gg(c, d, a, b, x[i+ 7], 14, 1735328473); + b = this.md5_gg(b, c, d, a, x[i+12], 20, -1926607734); + + a = this.md5_hh(a, b, c, d, x[i+ 5], 4 , -378558); + d = this.md5_hh(d, a, b, c, x[i+ 8], 11, -2022574463); + c = this.md5_hh(c, d, a, b, x[i+11], 16, 1839030562); + b = this.md5_hh(b, c, d, a, x[i+14], 23, -35309556); + a = this.md5_hh(a, b, c, d, x[i+ 1], 4 , -1530992060); + d = this.md5_hh(d, a, b, c, x[i+ 4], 11, 1272893353); + c = this.md5_hh(c, d, a, b, x[i+ 7], 16, -155497632); + b = this.md5_hh(b, c, d, a, x[i+10], 23, -1094730640); + a = this.md5_hh(a, b, c, d, x[i+13], 4 , 681279174); + d = this.md5_hh(d, a, b, c, x[i+ 0], 11, -358537222); + c = this.md5_hh(c, d, a, b, x[i+ 3], 16, -722521979); + b = this.md5_hh(b, c, d, a, x[i+ 6], 23, 76029189); + a = this.md5_hh(a, b, c, d, x[i+ 9], 4 , -640364487); + d = this.md5_hh(d, a, b, c, x[i+12], 11, -421815835); + c = this.md5_hh(c, d, a, b, x[i+15], 16, 530742520); + b = this.md5_hh(b, c, d, a, x[i+ 2], 23, -995338651); + + a = this.md5_ii(a, b, c, d, x[i+ 0], 6 , -198630844); + d = this.md5_ii(d, a, b, c, x[i+ 7], 10, 1126891415); + c = this.md5_ii(c, d, a, b, x[i+14], 15, -1416354905); + b = this.md5_ii(b, c, d, a, x[i+ 5], 21, -57434055); + a = this.md5_ii(a, b, c, d, x[i+12], 6 , 1700485571); + d = this.md5_ii(d, a, b, c, x[i+ 3], 10, -1894986606); + c = this.md5_ii(c, d, a, b, x[i+10], 15, -1051523); + b = this.md5_ii(b, c, d, a, x[i+ 1], 21, -2054922799); + a = this.md5_ii(a, b, c, d, x[i+ 8], 6 , 1873313359); + d = this.md5_ii(d, a, b, c, x[i+15], 10, -30611744); + c = this.md5_ii(c, d, a, b, x[i+ 6], 15, -1560198380); + b = this.md5_ii(b, c, d, a, x[i+13], 21, 1309151649); + a = this.md5_ii(a, b, c, d, x[i+ 4], 6 , -145523070); + d = this.md5_ii(d, a, b, c, x[i+11], 10, -1120210379); + c = this.md5_ii(c, d, a, b, x[i+ 2], 15, 718787259); + b = this.md5_ii(b, c, d, a, x[i+ 9], 21, -343485551); + + a = this.safe_add(a, olda); + b = this.safe_add(b, oldb); + c = this.safe_add(c, oldc); + d = this.safe_add(d, oldd); + } + return [a, b, c, d]; + }, + /* + * These functions implement the four basic operations the algorithm uses. + */ + md5_cmn: function(q, a, b, x, s, t) { + return this.safe_add(this.bit_rol(this.safe_add(this.safe_add(a, q), + this.safe_add(x, t)), s),b); + }, + md5_ff: function(a, b, c, d, x, s, t) { + return this.md5_cmn((b & c) | ((~b) & d), a, b, x, s, t); + }, + md5_gg: function(a, b, c, d, x, s, t) { + return this.md5_cmn((b & d) | (c & (~d)), a, b, x, s, t); + }, + md5_hh: function(a, b, c, d, x, s, t) { + return this.md5_cmn(b ^ c ^ d, a, b, x, s, t); + }, + md5_ii: function(a, b, c, d, x, s, t) { + return this.md5_cmn(c ^ (b | (~d)), a, b, x, s, t); + }, + /** + * Calculate the HMAC-MD5, of a key and some data + */ + core_hmac_md5: function(key, data) { + var bkey = this.str2binl(key), + ipad = Array(16), + opad = Array(16); + if (bkey.length > 16) + bkey = this.core_md5(bkey, key.length * this.chrsz); + + for (var i = 0; i < 16; i++) { + ipad[i] = bkey[i] ^ 0x36363636; + opad[i] = bkey[i] ^ 0x5C5C5C5C; + } + + return this.core_md5(opad.concat( + this.core_md5(ipad.concat(this.str2binl(data)), 512 + data.length * this.chrsz) + ), 512 + 128); + }, + /** + * Add integers, wrapping at 2^32. This uses 16-bit operations internally + * to work around bugs in some JS interpreters. + */ + safe_add: function(x, y) { + var lsw = (x & 0xFFFF) + (y & 0xFFFF), + msw = (x >> 16) + (y >> 16) + (lsw >> 16); + return (msw << 16) | (lsw & 0xFFFF); + }, + /** + * Bitwise rotate a 32-bit number to the left. + */ + bit_rol: function(num, cnt) { + return (num << cnt) | (num >>> (32 - cnt)); + }, + /** + * Convert a string to an array of little-endian words + * If chrsz is ASCII, characters >255 have their hi-byte silently ignored. + */ + str2binl: function(str) { + var bin = [], i, + mask = (1 << this.chrsz) - 1; + for (i = 0; i < str.length * this.chrsz; i += this.chrsz) + bin[i >> 5] |= (str.charCodeAt(i / this.chrsz) & mask) << (i%32); + return bin; + }, + /** + * Convert an array of little-endian words to a string + */ + binl2str: function(bin) { + var str = [], i, + mask = (1 << this.chrsz) - 1; + for (i = 0; i < bin.length * 32; i += this.chrsz) + str.push(String.fromCharCode((bin[i>>5] >>> (i % 32)) & mask)); + return str.join(""); + }, + /** + * Convert an array of little-endian words to a hex string. + */ + binl2hex: function(binarray) { + var hex_tab = this.hexcase ? "0123456789ABCDEF" : "0123456789abcdef", + str = [], i; + for (i = 0; i < binarray.length * 4; i++) { + str.push(hex_tab.charAt((binarray[i>>2] >> ((i%4)*8+4)) & 0xF) + + hex_tab.charAt((binarray[i>>2] >> ((i%4)*8 )) & 0xF)); + } + return str.join(""); + }, + /** + * Convert an array of little-endian words to a base-64 string + */ + binl2b64: function(binarray) { + var tab = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/", + str = [], i; + for(i = 0; i < binarray.length * 4; i += 3) { + var triplet = (((binarray[i >> 2] >> 8 * ( i %4)) & 0xFF) << 16) + | (((binarray[i+1 >> 2] >> 8 * ((i+1)%4)) & 0xFF) << 8 ) + | ((binarray[i+2 >> 2] >> 8 * ((i+2)%4)) & 0xFF); + for (var j = 0; j < 4; j++) { + if (i * 8 + j * 6 > binarray.length * 32) + str.push(this.b64pad); + else + str.push(tab.charAt((triplet >> 6*(3-j)) & 0x3F)); + } + } + return str.join(""); + } +}; + + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/core/crypto/rsa.js)SIZE(5048)TIME(Fri, 14 Oct 2011 08:01:44 GMT)*/ + +/** + * RSA, a suite of routines for performing RSA public-key computations in + * JavaScript. + * + * Requires BigInt.js and Barrett.js. + * + * Copyright 1998-2005 David Shapiro. + * + * You may use, re-use, abuse, copy, and modify this code to your liking, but + * please keep this header. + * + * Thanks! + * + * @author Dave Shapiro + */ + + + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/core/crypto/sha1.js)SIZE(5258)TIME(Fri, 14 Oct 2011 08:01:44 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + + +(function(global) { + +function rotate_left(n,s) { + var t4 = ( n<>>(32-s)); + return t4; +}; + +/* +function lsb_hex(val) { // Not in use; needed? + var str=""; + var i; + var vh; + var vl; + + for( i=0; i<=6; i+=2 ) { + vh = (val>>>(i*4+4))&0x0f; + vl = (val>>>(i*4))&0x0f; + str += vh.toString(16) + vl.toString(16); + } + return str; +}; +*/ + +function cvt_hex(val) { + var str=""; + var i; + var v; + + for( i=7; i>=0; i-- ) { + v = (val>>>(i*4))&0x0f; + str += v.toString(16); + } + return str; +}; + +global.SHA1 = function(str) { + // Calculate the sha1 hash of a string + // + // version: 905.3122 + // discuss at: http://phpjs.org/functions/sha1 + // + original by: Webtoolkit.info (http://www.webtoolkit.info/) + // + namespaced by: Michael White (http://getsprink.com) + // + input by: Brett Zamir (http://brett-zamir.me) + // + improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net) + // - depends on: utf8_encode + // * example 1: sha1('Kevin van Zonneveld'); + // * returns 1: '54916d2e62f65b3afa6e192e6a601cdbe5cb5897' + var blockstart, i, j, W = new Array(80), + H0 = 0x67452301, + H1 = 0xEFCDAB89, + H2 = 0x98BADCFE, + H3 = 0x10325476, + H4 = 0xC3D2E1F0, + A, B, C, D, E, temp; + + str = apf.crypto.UTF8.encode(str); + var str_len = str.length, + word_array = []; + + for(i = 0; i < str_len - 3; i += 4) { + j = str.charCodeAt(i) << 24 | str.charCodeAt(i + 1) << 16 | + str.charCodeAt(i + 2) << 8 | str.charCodeAt(i + 3); + word_array.push(j); + } + + switch (str_len % 4) { + case 0: + i = 0x080000000; + break; + case 1: + i = str.charCodeAt(str_len - 1) << 24 | 0x0800000; + break; + case 2: + i = str.charCodeAt(str_len - 2) << 24 | str.charCodeAt(str_len - 1) + << 16 | 0x08000; + break; + case 3: + i = str.charCodeAt(str_len - 3) << 24 | str.charCodeAt(str_len - 2) + << 16 | str.charCodeAt(str_len - 1) << 8 | 0x80; + break; + } + + word_array.push( i ); + + while((word_array.length % 16) != 14) + word_array.push( 0 ); + + word_array.push(str_len >>> 29); + word_array.push((str_len << 3) & 0x0ffffffff); + + for (blockstart = 0; blockstart < word_array.length; blockstart += 16) { + for (i = 0; i < 16; i++) + W[i] = word_array[blockstart + i]; + for (i = 16; i <= 79; i++) + W[i] = rotate_left(W[i - 3] ^ W[i - 8] ^ W[i - 14] ^ W[i - 16], 1); + + A = H0; + B = H1; + C = H2; + D = H3; + E = H4; + + for (i = 0; i <= 19; i++) { + temp = (rotate_left(A, 5) + ((B & C) | (~B & D)) + E + W[i] + + 0x5A827999) & 0x0ffffffff; + E = D; + D = C; + C = rotate_left(B, 30); + B = A; + A = temp; + } + + for (i = 20; i <= 39; i++) { + temp = (rotate_left(A, 5) + (B ^ C ^ D) + E + W[i] + 0x6ED9EBA1) + & 0x0ffffffff; + E = D; + D = C; + C = rotate_left(B, 30); + B = A; + A = temp; + } + + for (i = 40; i <= 59; i++) { + temp = (rotate_left(A, 5) + ((B & C) | (B & D) | (C & D)) + E + W[i] + + 0x8F1BBCDC) & 0x0ffffffff; + E = D; + D = C; + C = rotate_left(B, 30); + B = A; + A = temp; + } + + for (i = 60; i <= 79; i++) { + temp = (rotate_left(A, 5) + (B ^ C ^ D) + E + W[i] + 0xCA62C1D6) + & 0x0ffffffff; + E = D; + D = C; + C = rotate_left(B, 30); + B = A; + A = temp; + } + + H0 = (H0 + A) & 0x0ffffffff; + H1 = (H1 + B) & 0x0ffffffff; + H2 = (H2 + C) & 0x0ffffffff; + H3 = (H3 + D) & 0x0ffffffff; + H4 = (H4 + E) & 0x0ffffffff; + } + + temp = cvt_hex(H0) + cvt_hex(H1) + cvt_hex(H2) + cvt_hex(H3) + cvt_hex(H4); + return temp.toLowerCase(); +}; + +})(apf.crypto); + + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/core/debug/debug.js)SIZE(9811)TIME(Fri, 14 Oct 2011 08:01:44 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/core/debug/debugwin.js)SIZE(42735)TIME(Fri, 14 Oct 2011 08:01:44 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/core/debug/profiler.js)SIZE(24827)TIME(Fri, 14 Oct 2011 08:01:44 GMT)*/ + + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/core/parsers/js.js)SIZE(9016)TIME(Fri, 14 Oct 2011 08:01:44 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + + +/** + * Object returning an implementation of a JavaScript parser. + * + * @constructor + * @parser + * + * @author Ruben Daniels (ruben AT ajax DOT org) + * @version %I%, %G% + * @since 0.9 + */ +apf.JsParser = new (function(){ + var tok_lut = { + '"': 4, '\'': 4, '[': 2, ']': 3, '{': 2, '}': 3, '(': 2, ')': 3, + '\n': 6, '\r\n': 6, '//': 7, '/*': 7, '*/': 7, '/':8 + }, + tok_close = {'}': '{', ']': '[', ')': '('}, + tokenizerx = /(\r?[\n]|\/[\/*]|\*\/|["'{(\[\])}\]\/])|([ \t]+)|([\w\$._])+|(\\?[\w._?,:;!=+-\\\/^&|*"'[\]{}()%$#@~`<>])/g; + + function errToString(){ + return this.t + '[' + this.pos + ']'; + } + + function posToString(){ + return 'Ln : ' + this.line + ', Col : ' + this.col; + } + + function argToString(){ + return this.type + ' ' + this.name + + (this.def !== undefined ? '=' + this.def : ''); + } + + function markerToString(){ + return 'Marker:' + this.type + ' [' + this.pos + '] '+ (this.args ? '(' + + this.args.join(',') + ')' : ''); + } + + function funcToString(){ + return this.type + ' ' + this.ret + ' ' + this.name + (this.args ? '(' + + this.args.join(',') + ')' : '') + ' [' + this.pos + ']' + + (this.doc ? (' Doc: ' + this.doc) : ''); + } + + function concat(t, b, e, m){ + for (var s = [], sl = 0, i = b; i < e; i += 3) { + s[sl++] = (t[i] == 2) + ? concat(t[i + 2], 0, t[i + 2].length) + : t[i + 2]; + if(m && i blocks are processed as code. + * LiveMarkup features: + *
    + *
  1. inline xpaths
  2. + *
  3. E4X-like xml literals
  4. + *
  5. automatic statement/expression output and concat
  6. + *
  7. code and xpath expansion in-strings
  8. + *
  9. virtual-sync of async calls
  10. + *
+ * Examples: + * + * var x = [folder/@name]; // value of xpath + * [folder/@name] = 'hello'; // set an attribute + * [folder/file] += ; // add a new file node to folder/file list + * var y = // e4x-style xml + * x; //automatic output + * ; // automatic output + * if(a)func(); // automatic output of function returnvalue + * x = 15; // not outputted, assignments are never output. + * var z = "string with jsvars: {x} and xpath: [folder/@name]"; + * alert(comm.someRpcCall(x)); // async call returns sync value + * + * LiveMarkup syntax has one conflict with normal JS syntax; an array of 1 item vs xpath. + * Arrays are recognised if there is atleast one , present: [1,2] and 1 item: [,1] + * + * Another important feature of LiveMarkup is that its infinitely nestable: + * Outer construct: inner constructs + *
    + *
  1. string: xpath, code
  2. + *
  3. xpath: code, xpath(its a sub-xpath when NOT after [)\w] or IS inside "str" or 'str' )
  4. + *
  5. code: xpath, code, string, xml
  6. + *
  7. xml: xpath, code
  8. + *
+ * Example of code in xpath in xml in code in string, for the sake of argument: + * + * var x = "str{}" + * + * since code has an auto-output, it is also possible to use scope { } delimiters holding a value + * and used as an expression. + * var x = {..code with auto output..} + * The ^ character at the beginning of a statement can force 'no output' but is very rarely needed. + * + * It is important to realise that Live Markup is converted to normal Javascript + * in a single compile pass, and does not constitute black-magic. + * As rarely debugging might force you to look at generated code, its useful to know it exists. + * For instance: + * XML literals are turned into normal JS strings: becomes "" + * in generated code. This is different from E4X where they are special type of object. + * xpaths and operators are turned into functioncalls: [xpath] becomes _val(_n,"xpath") + * and nesting becomes concatenation: "a{x}b" becomes ("str"+(x)+"str") + * + * Live markup xpath reference + * Different xpath types: + * [xpath] - value xpath (string) + * %[xpath] - single node + * *[xpath] - node set + * #[xpath] - number of nodes selected by xpath + * $[symbol] - language 'xpath', fetches value from language symbol library + * [model::xpath] - xpath value on model with name 'model' + * [{x}::xpath] - xpath value on xml node or model with js variable x + * [{rpc.thing()}::xpath] - xpath value on an rpc call + * [xpath] = 'value' - assign text-value to xpath (nodeValue = ..) + * [xpath] = - replace node with right hand xml + * [xpath] = xmlNode - replace node with right hand node, removing xmlNode from its old position + * [xpath] += 'value' - appends text-value to nodeValue + * [xpath] += - appends the after the selected node + * [xpath] += xmlNode - appends the node and removes from its old position + * + * Macro reference + * localName(n) - returns the localName of the context node or supplied argument + * tagName(n) - tagName of context node or supplied argument + * nodeValue(n) - value of context nore or supplied argment similar to [.] + * local(n){..code..} - a codeblock with a new context node n, n can be xml-string too + * each(set){..code..) iterates over the set. usually used as: each(*[xpath]){} + * + */ + +/** + * @constructor + * @parser + * + * @author Rik Arends + * @version %I%, %G% + * @since 3.0 + */ +apf.lm = new (function(){ + + var statement_lut = { // all js statements to see its NOT an expression + "var": 1, "for": 1, "while": 1, "do": 1, "if": 1, "else": 1, + "switch": 1, "case": 1, "break": 1, "continue": 1, "default": 1, + "function":2, "return": 1, "try": 1, "catch": 1, "throw":1, + "debugger": 1, "alert": 1, "confirm": 1,"setTimeout": 1,"setInterval": 1,"delete": 1, "export": 1, "import": 1, + "label": 1, "foreach":1, "each": 1, "eachrev":1, "foreachrev":1, "node": 1, "local": 1, "yield": 1, + "let":1, "finally":1, "delete":1 + }, + type_lut = { // used for optimizing the parse regexp + "\n": 1, "\r\n": 1, "==":2, "++":2, "--":2, '"': 5, "'": 5, + "": 6, "/*": 6, "//": 6, "*/": 6, "{": 7, "}": 8, + "[": 9, "]": 10, "(": 11, ")": 12, "<": 13, ">": 14, "+=":2, + "-=":2, "/=":2, "*=":2, "!=":2 + }, + type_close = { // handy + "{": "}", "[": "]", "(": ")", "{{":"}" + }, + xpath_axes = { // used to detect xpath axes or model + "ancestor": 1, "ancestor-or-self": 1, "attribute": 1, "child": 1, + "descendant": 1, "descendant-or-self": 1, "following": 1, + "following-sibling": 1, "namespace": 1, "parent": 1, "preceding": 1, + "self": 1 + }, + misc_tok = { // misc token lookup + ";":1, ",":2, "^":3, "=":4, "+=":4, "-=":4, "/=":4, "*=":4, "/":5, ":":6 + }, + xpath_lut_code = { // which autoxpath to use when doing macro({xpath}) + "~": "_val(_n,", "%": "_nod(_n,", "*": "_nods(_n,", "#": "_cnt(_n,", "$": "_lng(" + }, + xpath_lut_text = { // which autoxpath to use when doing xpath macros in textmode + "~": "_val(_n,", "%": "_xml(_n,", "*": "_xmls(_n,", "#": "_cnt(_n,", "$": "_lng(" + }, + xpath_lut_attr = { // xpath lut for node attributes + "~": "_val(_n,", "%": "_val(_n,", "*": "_val(_n,", "#": "_cnt(_n,", "$": "_lng(" + }, + xpath_lut_node, + xpath_lut_node_normal = { // normal xpath lookup + "~": "_val(_n,", "%": "_xml(_n,", "*": "_xmls(_n,", "#": "_cnt(_n,", "$": "_lng(" + }, + xpath_lut_node_langedit = { // language edit xpath lookup + "~": "_val(_n,", "%": "_xml(_n,", "*": "_xmls(_n,", "#": "_cnt(_n,", "$": "_lnged(" + }, + pre_regexp = { + "[":1, "(":1, ",":1, "=":1, "return":1, "throw":1 + }, + pre_xpath = { + "else":1, "return":1, "delete":1 + }, + pre_plain = { + "do":1, "else":1, "try":1 + }, + op_lut = { // obj.prop += operator lut + "=" : "_asn(", "+=": "_add(", "-=": "_sub(", "/=": "_div(", "*=": "_mul(" + }, + new_block = { + "+":1, "%":1, "-":1, "/":1, "=":1, "(":1, "?":1, "|":1, "^":1, "[":1, + "&":1, "*":1, "!":1, ":":1, "<":1, ",":1 + }, + out_context_word = { // token preceeding a word signalling a new output + "{":1, "} ":1, ")":1, ") ":1, ";":1, "\n":1, "else":1 + }, + out_context_paren = { // token preceeding a paren signalling a new output + "{":1, ";":1, "\n":1, "else":1 + }, // special markers: ') ' tail of xpath macro. ') ' func def, tok=') ' its not an if while etc. + markup_in_code_lut = { + "} ":1, ") ":1,// the } used when it wasnt a code-expression + "(":1, /*")":1,*/ ";":1, "&":1, "^":1, "|":1, ",":1, '"':1, "'":1, "=":1, + "!=":2,"+=":2, "-=":2, "/=":2, "*=":2, "?":1, "{":1, "}":1, ">":1, "[":1, + /*"]":1,*/ "+":1, ":":1, "else":1, "return":1 + }, + block_autoappend = { // token preceeding block signalling auto append + '"':1, "'":1, ">":1, "]":1, "}":1 + }, + unesc_lut = { // unescape in code and xpath mode + "\\\"": "\"", "\\\'": "\'", "\\{": "{", "\\}": "}", "\\[": "[", + "\\]": "]", "\\(":"(", "\\)":")", "\\\\":"\\" + }, + call_exclusion = { + "alert": 1, "confirm" :1, "setTimeout":1, "setInterval":1, "switch":1, + "call":1, "return":1, "throw":1, "case":1, "catch":1, + "abs":1,"acos":1,"asin":1,"atan":1,"atan2":1,"ceil":1, + "cos":1,"exp":1,"floor":1,"log":1,"max":1,"min":1, + "pow":1,"random":1,"round":1,"sin":1,"sqrt":1,"tan":1,"lin":1,"linear":1, + "idx":1,"sort":1,"typeof":1 + }, + is_out_space = { + " ":1, "\n":1 + }, + newline_notallowed = { + "{":1, ";":1, "(":1, "\n":1 + },//@todo !verify and document! character escaping system + unesc_str = { // unescape in string mode + "\\{": "{", "\\}": "}", "\\[": "[", "\\]": "]", "\\(": "(", "\\)": ")" + }, + unesc_txt = { // unescape in text mode + "\\{" : "{", "\\}" : "}", "\\[" : "[", "\\]" : "]", "\\(" : "(", + "\\)" : ")", "\\\\": "\\\\\\\\", "\\" :"\\\\", "\\<" : "<", "\\>" : ">" + }, + xml_code_operators = { // word to operand lookup table for easy use in xml + "lte": "<=", "gte": ">=", "lt": "<", "gt": ">", "and": "&&", "or": "||", + "andbin": "&", "orbin": "|", "LTE": "<=", "GTE": ">=", "LT": "<", + "GT": ">", "AND": "&&", "OR": "||", "ANDBIN": "&", "ORBIN": "|" + }, + xpath_macro = { // which autoxpath to use when doing macro({xpath}) + 0 : "_val(_n,", + 1 : "_valcr(_n,_cr,", + 2 : "_nod(_n,", + 3 : "_nodcr(_n,_cr,", + 4 : "_nods(_n,", + 5 : "_xpt(_n,", + 6 : "_valst(_n,", + 7 : "_valed(_n,", + 8 : "_valattr(_n,", + "foreach" : "_nods(_n,", + "each" : "_nods(_n,", + "foreachrev": "_nods(_n,", + "eachrev" : "_nods(_n,", + "xabs" : "_valst(_n,", + // "edit" : "_argwrap(_n,", + // "edit" : "_val(_n,", // toggled by liveedit + "local" : "_nod(_n,", + "tagName" : "_nod(_n,", + "localName" : "_nod(_n,", + "xml" : "_xmlq(", + "_call" : "_val(_n," + }, + xpath_model = { // which autoxpath to use when doing macro({xpath}) + "_val(_n," : "_valm(", + "_valcr(_n,_cr,": "_valcr(0,_cr,", + "_nod(_n," : "_nodm(", + "_nodcr(_n,_cr,": "_nodcr(0,_cr,", + "_nods(_n," : "_nodsm(", + "_argwrap(_n," : "_argwrapm(", + "_xml(_n," : "_xml(0,", + "_xmls(_n," : "_xmls(0,", + "_cnt(_n," : "_cntm(", + "_xpt(_n," : "_xptm(", + "_valst(_n," : "_valm(", + "_valed(_n," : "_valed(0,", + "_lng(" : "_lng(", + "_lnged(" : "_lnged(" + }, + parserx = /(\r?[\n]|\/\*|\*\/|\/\/|\<\!\-\-|\-\-\>|[=\!+\/\*-]=|\+\+|\-\-|["'{(\[\])}\]\<\>]|$)|([ \t]+)|([a-zA-Z\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF.$_][\w.$_]*)|(\d[x\d.]*)|(\\?[\w._?,:;!=+-\\\/^&|*"'[\]{}()%$#@~`<>]?)/g, + selfrx = /(^|\|)(?!\@|text\(\)|\.\.|[\w\-\:]+?\:\:)/g, // inject self regexp + macro_o = {}, + macro_c = {}, + macro_m = {}, + // config vars + c_async_lut = apf.$asyncObjects || { // used to figure out if the thing before the. is an async obj + "comm" :1, + "rpc" :1, + "http" :1, + "apf.ajax" :1 + }, + c_process_async, + c_xpathmode, // guess 'node' as the type for {} o_xpathpairs, 1 = node, 2 = nodes + c_elemxpath, // which xpath macro to use inside an element + c_statexpath, // which xpath to use for the stateful value + c_injectself, // add self:: in some o_xpathpairs + c_propassign, // support property assigns + c_export, // export function to some object + // outputs + o, ol, // output and output len + o_asyncs, // number of async calls + o_xpathpairs, // all xpaths and their models in pairs + o_props, // the js properties found + o_segs, // segments at groundlevel + o_xpaths, // xpaths at groundlevel + o_models, // number of xpaths with models + // temp and state vars + s = [], sl, // scopestack and scopestack len + bt = [], // backtrack lut + bts = [], // backtrack string stack + parse_mode, // the parse parse_mode + scope, // ol of a scope begni + segment, // the ol of a segment begin + start_tok, // the token a string or comment was started with + str,str_len, // length of the input string + line_no, // line number we are at + nesting, // nesting count + // last state vars + last_tok, // last token + last_type, // last type + last_dot, // . pos when last token was a word + last_model, // last model found + last_prop, // last property found + last_cmt_mode, // the parse mode outside the comment + last_cmt_tok, // last token before comment + last_cmt_type, // last type before comment + last_line, // offset of last newline + last_ns, // last namespace found + last_word; // last word in code mode + // macros used in code() + macro_o["if"] = "if(", + macro_c["if"] = ")", + macro_o["while"] = "while(", + macro_c["while"] = ")", + macro_o["for"] = "for(", + macro_c["for"] = ")", + macro_o["switch"] = "switch(", + macro_c["switch"] = ")", + macro_o["catch"] = "catch(", + macro_c["catch"] = ")", + macro_c["function"] = ") "; + + macro_o.foreach = + macro_o.each = "\nfor(var _t=_t||[],_t=(_t.push(_n,0,(", + macro_c.foreach = + macro_c.each = ")||[]),_t);(_n=_t[_t.length-1][_t[_t.length-2]++])||(_t.length-=2,_n=_t.pop(),0);)", + macro_o.foreachrev = + macro_o.eachrev = "\nfor(var _t=_t||[],_t=(_t.push(_n,0,(", + macro_c.foreachrev = + macro_c.eachrev = ")||[]),_t);(_n=_t[_t.length-1][_t[_t.length-1].length-(_t[_t.length-2]++)-1])||(_t.length-=2,_n=_t.pop(),0);)", + + macro_o.local = "\nfor(var _t=_t||[],_t=(_t.push(_n,((_n=_local(", + macro_c.local = ")),1)),_t);(_t[_t.length-1]--&&_n)||(_t.length--,_n=_t.pop(),0);)", + macro_o._editlm = "_valedx(true, ", // only serves to switch default xpath in edit([xpath]) + macro_o._editnormal = "_valedx(false, ", // only serves to switch default xpath in edit([xpath]) + macro_c.edit = ")", + macro_o.xabs = " ( ", + macro_c.xabs = " ) ", + + macro_o.localName = "_localName(_n", + macro_c.localName = ")", + macro_o.output = "_o.join(''", + macro_c.output = ")", + macro_o.reset = "(_o=[],l=0", + macro_c.reset = ")", + macro_o.index = "apf.getChildNumber.call(apf", + macro_c.index = ")", + macro_o.item = "(_t[_t.length-1][_t[_t.length-2]-1]", + macro_c.item = ")", + macro_o.first = "(_t[_t.length-2]==1", + macro_c.first = ")", + macro_o.last = "(_t[_t.length-2]==_t[_t.length-1].length", + macro_c.last = ")", + macro_o.total = "(_t[_t.length-1].length", + macro_c.total = ")", + macro_o.pos = "(_t[_t.length-2]-1", + macro_c.pos = ")", + + macro_o.tagName = "_tagName(_n", + macro_c.tagName = ")", + macro_o._nodeValue = "_nodeValue(_n", + macro_c._nodeValue = ")", + macro_c.async = "])", + macro_c.precall = "])", + macro_c._call = ")"; + + var call_args_lut = { + _call : ".call(_n", + localName : macro_o.localName, + tagName : macro_o.tagName, + nodeValue : macro_o.nodeValue, + index : macro_o.index + }, + + // centralized code fragments used in parser/generator + cf_block_o = "(function(){var _o=[],_l=0;\n", + cf_block_c = ";return _l==1?_o[0]:_o.join('');})()", + cf_async_o = "_async(_n,_c,_a,_w,_f,this,", + cf_async_m = "',_a[++_a.i]||[", + cf_obj_output = "_r=", + cf_mode_output, + cf_str_output = "_o[_l++]=", + cf_def_output = "", + cf_func_o = "{var _o=[],_l=0,_n=this;\n", + cf_func_c = ";\nreturn _l==1?_o[0]:_o.join('');}", + + // compile chunks used in compile/match + cc_async_o = "(_a=_a||{}).i=0;try{\n", + cc_async_c = "}catch(_e){if(_e.x)return;throw(_e);}\n", + //cc_async_o = "(_a=_a||{}).i=0;", + //cc_async_c = "", + cc_pc_o = "(_a=_a||{}).i=0;try{_precall(_w);", + cc_pc_c = "}catch(_e){if(_e.x)return;throw(_e);}", + cc_opt_o = "with(_w){", + cc_opt_c = "}", + cc_v_blk_o = "var _o=[],_l=0;_o[_l++]=", + cc_v_blk_ob = "var _o=[],_l=0;\n", + cc_v_blk_c = ";\nreturn _ret(_l==1?_o[0]:_o.join(''));", + cc_v_blk_cb = ";\n_c(_ret(_l==1?_o[0]:_o.join('')),apf.SUCCESS,apf.$lmx);apf.$lmx=null;", + cc_v_ret_o = "return _ret(", + cc_v_ret_c = ");", + cc_v_cb_o = "_c(_ret(", + cc_v_cb_c = "),apf.SUCCESS,apf.$lmx);apf.$lmx=null;\n", + + cc_o_blk_o = "var _r=", + cc_o_blk_ob = "var _r;", + + cc_o_blk_c = ";\nreturn _r;", + cc_o_blk_cb = ";\n_c(_r,apf.SUCCESS,apf.$lmx);apf.$lmx=null;", + cc_o_blk_ce = ";\n_c(0,apf.SUCCESS,apf.$lmx);apf.$lmx=null;;", + cc_o_ret_o = "return ", + cc_o_ret_c = "", + cc_o_cb_o = "_c(", + cc_o_cb_c = ",apf.SUCCESS);", + cc_f_async_o = "var _f=function(_n,_c,_w,_a){", + cc_f_opt_o = "var _f=function(_n,_w){", + cc_f_o = "var _f=function(_n){", + cc_fc_async_o = "var _f=function(_n,_c,_w,_cr,_a){", + cc_fc_opt_o = "var _f=function(_n,_w,_cr,){", + cc_fc_o = "var _f=function(_n,_cr){", + cc_fe_async_o = "var _f=function(event,_c,_w,_a,_n){", + cc_fe_opt_o = "var _f=function(event,_w,_n){", + cc_fe_o = "var _f=function(event,_n){", + cc_f_c = "}", + cc_f_match_o = "var _f=function(_m){", + + cc_m_m_blk = ";\nif(_n=_r){if(!_n.nodeType)_n=_m;", + cc_m_m_value_o = ";\nif(_n=", + cc_m_m_value_c = "){if(!_n.nodeType)_n=_m;", + cc_m_v_string = "\nreturn ", + cc_m_v_o = "\nreturn _ret(", + cc_m_v_c = ");", + cc_m_n_string = "\nreturn _n;", + cc_m_n_o = "\nreturn (_r = (", + // decision point for compileMatch node-mode for the return type + cc_m_n_c = "))?(_r.nodeType?_r:_n):(_r===null?null:_n);", + + cc_m_o = "var _r, _n = _m;", + cc_m_brk = ";\n_n = _m;", + cc_m_v_ret = "\nreturn _ret(_nodeValue(_n));" , + cc_m_n_ret = "\nreturn _n;" , + cc_m_c = "\n}"; + + function switchToBlock(no_output){ // used to switch expression mode to block mode + var u, v; + if (o[scope-1] == "{{") + u = scope-1; // scan for our root expression block to switch to block + else + for (v = sl - 2, u = 0; v >= 0 && o[u=(s[v] & 0xfffffff) - 1] != "{{"; v -=2 ){}; + + if (!no_output && ol > u + 1) // inject auto output unless no output or nothing to output in buffer + o[u] = cf_block_o + cf_str_output + else + o[u] = cf_block_o; + parse_mode = 1; + } + + function parser(tok, rx_lut, rx_white, rx_word, rx_num, rx_misc, pos){ + var u, v, w, + type = rx_lut ? type_lut[rx_lut] : (rx_white ? 0 : (rx_word ? 3 : (rx_num ? 4 : (tok ? 2 : 15)))); + switch (parse_mode) { + case 0: // ===================== expression parse_mode ========================= + case 1: // ========================== block parse_mode ========================= + switch (type) { + case 0: // -------- whitespace -------- + if ((last_type == 3 && last_tok!='$') || last_type == 4) + o[ol++] = " "; + else if(xpath_lut_code[last_tok]) + last_type = 0;// make last_type visible to xpathmode select + break; + case 1: // -------- newline -------- + line_no++, + last_line = pos; + if (o[ol-1] != "\n" && !newline_notallowed[last_tok]) + o[ol++] = "\n"; + if (xpath_lut_code[last_tok]) + last_type = 0;// make last_type visible to xpathmode select + break; + case 2: // -------- misc -------- + if (v = misc_tok[tok]) { + switch (v) { + case 1: // ';' + if (!s[sl-1]) {// close = macro + o[ol++] = ")", + sl -= 2; + } + + if (!parse_mode) { // dont do ; inject newline instead + if (o[ol-1] != "\n" && last_tok != "{" && last_tok != ";") + o[ol++] = "\n"; + } + else if(!sl || s[sl - 1]) // dont inject ; if we are in nested assignment macros + o[ol++] = ";"; + break; + case 2: // ',' + if (!s[sl - 1]) { // close = macro + o[ol++] = ")", + sl -= 2; + } + o[ol++] = ","; + break; + case 3: //'^' // dont output + if (o[ol-1] == "\n" || o[ol - 1] == ";" || last_tok=="{" + || last_tok == "} " || ol == scope) { // dont output-shortcut requirements + if (!parse_mode) + switchToBlock(); + o[ol++] = " "; // two spaces make it modify the output-ifs outcome + } + else + o[ol++] = "^"; + break; + case 4: //'= += -= assignment macro mode + if(last_tok!='<' && last_tok!='>'){ + // we should only switch to block when we are not in a ( ) scope + if (!parse_mode && o[scope-1]!='(') + switchToBlock(true); + o[ol++] = tok; + // lets scan in reverse to see if we have an output or a non-output + + for (v = ol; v >= scope && !statement_lut[o[v]] && !((o[v] == " " + || o[v] == (nesting ? cf_str_output : cf_mode_output)) && (o[v]="",1)); v--){}; + + if (last_type == 3 && last_dot>0 && last_tok.charAt(0)!="."){ // prop = macro + if(c_propassign){ + ol -= 2; + while (is_out_space[o[ol]]) + ol--; + w = last_tok; + o[ol++] = op_lut[tok], o[ol++] = w.slice(0,last_dot), + o[ol++] = ",'", o[ol++] = w.slice(last_dot+1), + o[ol++] = "',", s[sl++] = scope | (parse_mode << 28), + s[sl++] = ""; // notabene, this stored item is checked everywhere + } + } + }else{ + o[ol++] = tok; + }break; + case 5: // '/' // regexp mode + if (pre_regexp[last_tok]) { + s[sl++] = scope | (parse_mode << 28); + s[sl++] = o[ol++] = tok; + scope = segment = ol - 1; + nesting++, parse_mode = 5, start_tok = tok; + } + else + o[ol++] = "/"; + break; + case 6: // ':' // switch to {x:1} object mode + if (sl > 2 && s[sl - 1] == "{{" && (ol < scope + 4 && last_type == 5) + || (ol < scope + 3 && (last_type == 3 || last_type == 4))) { + o[scope-1] = s[sl-1] = "{" + parse_mode = (v = s[sl - 2]) >> 28; + s[sl-2] = v & 0xfffffff, + nesting--; + } + else if(o[ol - 3] == "case" || (last_type == 5 && last_word == "case")) + tok = ";"; //fixes auto output problem + o[ol++] = ":"; + break; + default: + o[ol++] = tok; + break; + } + } + else + o[ol++] = unesc_lut[tok] || tok; + break; + case 3: // -------- word -------- + case 4: // ------- number ------- + if( v = xml_code_operators[tok] ){ + o[ol++] = tok = v, type = 2; + } else { + v = u = w = 0;// last_word used for case 'bla bla': + last_dot = (last_word = tok).lastIndexOf("."); + if (tok.charAt(0) != '.' // .obj shouldnt trigger block + && ((v = (u = ((out_context_word[last_tok] // check if we need to switch + || o[ol - 1] == "\n") && !new_block[last_tok])) + && !s[sl - 1].indexOf("{") && ol > scope) + || (w = statement_lut[tok])) && !parse_mode){ // check statement + if(w == 2 && s[sl - 1].indexOf("{")) w = 0; // (function() shouldnt trigger blockmode + switchToBlock(w); // pass in statement_lut[tok] as outputflag + } + if (u && !s[sl - 1]) { // assign macro close + o[ol-1] == "\n" && (o[ol - 1] = ""), o[ol++] = ")", + o[ol++] = "\n", v = 1, sl -= 2; + } + if (v && parse_mode && !statement_lut[tok] && !call_exclusion[tok]) // inject output + o[ol++] = (nesting ? cf_str_output : cf_mode_output); + + if (last_dot > 0 && tok.charAt(0) != ".") // store property + o_props[o[ol++] = last_prop = tok] = 1; + else o[ol++] = tok; + } + break; + case 5: // -------- stringquotes -------- + if ((v = (u = ((out_context_word[last_tok] || o[ol - 1]== "\n" ) + && !new_block[last_tok])) && !s[sl - 1].indexOf("{") + && ol > scope) && !parse_mode) // check if we need to switch to block mode + switchToBlock(); + + if (u && !s[sl - 1]) { // close = macro + o[ol - 1] == "\n" && (o[ol - 1] = ""), o[ol++] = ")", + o[ol++] = "\n", v = 1, sl -= 2; + } + if (v) { // generate output + o[ol++] = (o[ol-2] != "\n" && block_autoappend[last_tok]) + ? "+" + : (nesting ? cf_str_output : cf_mode_output); + } + else if (block_autoappend[last_tok]) + o[ol++] = "+"; + + s[sl++] = scope | (parse_mode << 28), s[sl++] = o[ol++] = tok; + scope = segment = ol - 1, nesting++, parse_mode = 5, start_tok = tok; + break; + case 6: // -------- comment -------- + if (tok == "*/" || tok== "-->") + throw { + t: "Unmatched comment "+tok, + p: pos + }; + last_cmt_mode = parse_mode, last_cmt_tok = last_tok, + last_cmt_type = last_type, parse_mode = 6, start_tok = tok; + break; + case 7: // -------- { -------- + if (o[ol - 1] == ") " || (o[ol - 2] == ") " && ol--)) { // ') ' is function def + if (s[sl - 1] != "(" && s[sl - 1] != "[") { + s[sl++] = scope | (parse_mode << 28), + s[sl++] = "{{", o[ol++] = cf_func_o, + scope = ol, parse_mode = 1, nesting++, o[ol++] = ""; // make the scope check pass + } + else { + s[sl++] = scope, s[sl++] = o[ol++] = tok, scope = ol; + parse_mode = 1; + }// for do else..etc below + } + else if ((macro_o[s[sl + 1]] && last_tok == ") ") || pre_plain[last_tok]) { + s[sl++] = scope, s[sl++] = o[ol++] = tok, scope = ol; + o[ol++] = ""; + } + else { + if ((v = (u = ((out_context_word[last_tok]||o[ol - 1] == "\n") + && !new_block[last_tok])) + && !s[sl - 1].indexOf("{") && ol > scope) && !parse_mode) + switchToBlock(); // block mode detection + + if (u && !s[sl - 1]) { // close = macro + o[ol - 1] == "\n" && (o[ol - 1] = ""), + o[ol++] = ")", o[ol++] = "\n", v = 1, sl -= 2; + } + if (v) { // inject output, +''+ is when between two { } { } (supposedly) + o[ol++] = (o[ol - 2] != "\n" && block_autoappend[last_tok]) + ? "+''+" + : (nesting ? cf_str_output : cf_mode_output); + } + else if (block_autoappend[last_tok]) // inject append + o[ol++] = (last_tok == "}") ? "+''+" : "+"; + + s[sl++] = scope | (parse_mode << 28), s[sl++] = o[ol++] = "{{"; + + if (!nesting && scope != ol) // count output segments on nesting 0 + o_segs++; + + nesting++, segment = scope = ol, parse_mode = 0; + } + break; + case 8: // -------- } -------- + if (!s[sl - 1]) // close = macro + o[ol++] = ")", o[ol++] = "\n",sl -= 2; + + if (type_close[v = s[--sl]] != (o[ol++] = tok)) + throw { + t: "Cannot close " + v + " with " + tok, + p: pos + }; + + if (v == "{{") { // closing code block + if (scope == ol - 1) { + if( (s[sl - 1] >> 28) <= 1) // empty code in code + o[scope-1] = "{", o[ol - 1] = "}"; + else // empty code elsewhere + o[scope - 1] = o[ol - 1] = "'"; + } + else { + if (!parse_mode) { // expression wraps in () + o[scope - 1] = "(", + o[ol - 1] = ")"; + } + else { // codeblock wraps in (function(){})() + if (o[scope - 1] == cf_func_o) { + if (scope == ol - 2) + o[scope - 1] = "{", o[ol - 1] = "}"; + else + o[ol - 1] = cf_func_c; + } + else + o[ol - 1] = cf_block_c; + } + } + parse_mode = (v=s[--sl])>>28, scope = v&0x0fffffff; + segment = ol, nesting--; + + if(!nesting) // count segs on nesting level 0 + o_segs++; + + if (parse_mode == 7) // attribute hack + o[ol++] = "+\"\\\"", parse_mode = 4; + } else scope = s[--sl]; // was object def or if(){} + + break; + case 9: // -------- [ -------- + if (((last_type == 3 && !pre_xpath[last_tok] && last_tok!='$') || last_tok == ")" || last_tok == "]") && o[ol - 1] != "\n") { + o[ol++] = "[", s[sl++] = scope | (parse_mode << 28), //was array index + s[sl++] = tok, segment = scope = ol; + } + else { + last_model = null; + + if ((w = xpath_lut_code[last_tok])) { + ol--, last_tok = o[ol-1]; // xpath with *%$# + } + else { + w = xpath_macro[s[sl - 1]] || xpath_macro[nesting ? 0 : c_xpathmode]; + } + if ((v = (u = ((out_context_word[last_tok] || o[ol - 1] == "\n") + && !new_block[last_tok])) && !s[sl - 1].indexOf("{") + && (ol > scope || s[sl - 1].length == 1)) && !parse_mode) + switchToBlock(); // check if we need to switch to block mode + + if (u && !s[sl - 1]) { // close = macro + o[ol - 1] == "\n" && (o[ol - 1] = ""), o[ol++] = ")", + o[ol++] = "\n", v = 1, sl -= 2; + } + + if (v) { // inject output + o[ol++] = (o[ol - 2] != "\n" && block_autoappend[last_tok]) + ? "+" + : (nesting ? cf_str_output : cf_mode_output); + } + else if (block_autoappend[last_tok]) // inject append + o[ol++] = "+"; + + if(!nesting && ol!=scope) + o_segs++; + // store scope in bt for reparse of array + nesting++, s[sl++] = scope|(parse_mode<<28), s[sl++] = o[ol++] = w, + segment = scope = ol, bt[scope] = pos, parse_mode = 3; + } + break; + case 10: // -------- ] -------- + if(!s[sl-1]) // close = macro + o[ol++]=")",sl -=2; + + if ( type_close[v = s[--sl]] != (o[ol++] = tok)) + throw { + t: "Cannot close " + v + " with " + tok, + p: pos + }; + + scope = s[--sl]&0xfffffff; // clean scope of possible parse_mode 1 + break; + case 11: // -------- ( -------- + if ( ((v = (u=((out_context_paren[last_tok]||o[ol-1]=="\n") && + !new_block[last_tok])) && !s[sl-1].indexOf("{") && + ol>scope)) && !parse_mode) + switchToBlock(); + + if (u && !s[sl-1]) // close = macro + o[ol-1]=="\n"&&(o[ol-1]=""),o[ol++]=")", o[ol++]="\n",v = 1,sl -=2; + + if (v && parse_mode) // inject output + o[ol++] = (nesting?cf_str_output:cf_mode_output), last_type = 0; + + if (w = macro_o[last_tok]) { + if (o[ol-1]==" ") ol--; // support func () + o[ol-1] = w, s[sl++] = scope, s[sl++] = last_tok, scope = segment = ol; + } + else { + if (last_type == 3) { // word( + if (last_dot < 0) { // no dot + v = 0; + if (last_tok == "function" || o[ol - 3] == "function" || o[ol - 4] == "function") { + s[sl++] = scope, s[sl++] = "function", //func def + o[ol++] = "(", scope = segment = ol; + //TODO! check the depth with which functions are made global + if(last_tok!="function" && c_export && sl==4){ + o[v=(o[ol - 4] == "function")?(ol-4):(ol-5)] = + "var "+last_tok+" = "+c_export+"."+last_tok+" = function"; + o[v+2] = ""; + } + } + else { // its a call and not a new + if (!call_exclusion[last_tok] && o[ol-3]!="new") { + o[ol++] = ".call(_n", s[sl++] = scope, + s[sl++] = "_call", scope = segment = ol; + } + else { // its an excluded call + s[sl++] = scope, s[sl++] = o[ol++] = tok, + scope = segment = ol; + } + } + } + else { + if (last_dot > 1 && c_process_async && (c_async_lut[v = last_tok.substring(0,last_dot)] || c_async_lut[v = last_tok])) {// its an async call + if (o[--ol] == " ") + ol--; + o[ol++] = cf_async_o, o[ol++] = v, o[ol++] = ",'"; + o[ol++] = last_tok.slice(last_dot + 1); + o[ol++] = cf_async_m, s[sl++] = scope, s[sl++] = "async", + scope = segment = ol, o_asyncs++; + } + else { // its a obj.prop() type call + if(last_tok.indexOf('.')!=last_dot) // obj.prop.call(); + o_props[last_tok.slice(0,last_dot)] = 1; + + s[sl++] = scope, s[sl++] = o[ol++] = tok, + scope = segment = ol; + } + } + } + else { // function object call + s[sl++] = scope, s[sl++] = o[ol++] = tok, + scope = segment = ol; + } // dont store calls as props + if (last_tok == last_prop) + delete o_props[last_tok]; + } + break; + case 12: // -------- ) -------- + if (!s[sl - 1]) { // close = macro + o[ol-1] == "\n" && (o[ol-1] = ""), o[ol++] = ")", + o[ol++]="\n", v = 1, sl -= 2; + } + + if (w = macro_c[v = s[--sl]]) { // closing a macro + if (v != "_call") + tok = ") "; // make sure any [ ] doesnt get interpreted as array index + if ((u = call_args_lut[v]) && u != o[ol - 1]) + o[scope - 1] = u + ",";// do , insertion for argless macros + o[ol++] = w; // write close-end of macro + } + else if (type_close[v] != (o[ol++] = tok)) { + throw { + t:"Cannot close " + v + " with " + tok, + p: pos + }; + } + scope = s[--sl] & 0xfffffff; // scope should be unimpacted + break; + case 13: // -------- < -------- + // check if < is markup or not + if (ol == scope || markup_in_code_lut[last_tok] || o[ol - 1] == "\n"){ + if ((v = (u = ((out_context_word[last_tok] || o[ol - 1] == "\n") + && !new_block[last_tok])) && !s[sl - 1].indexOf("{") + && ol > scope) && !parse_mode) + switchToBlock(); // switch to block mode + + if (u && !s[sl - 1]) { // close = macro + o[ol - 1] == "\n" && (o[ol - 1] = ""), o[ol++] = ")", + o[ol++] = "\n", v = 1, sl -= 2; + } + if (v) { + o[ol++] = (o[ol - 2] != "\n" && block_autoappend[last_tok]) + ? "+''+" + : (nesting ? cf_str_output : cf_mode_output); + } + else if (block_autoappend[last_tok]) + o[ol++] = "+"; + // start markup mode with the markup-stack counting + last_ns = null, o[ol++] = '"', o[ol++] = "<", nesting++, + s[sl++] = scope | (parse_mode << 28), sl += 3, + s[sl - 2] = s[sl - 1] = 0; + + segment = scope = ol - 1, parse_mode = 4; + } + else + o[ol++] = "<"; + break; + case 14: // -------- < -------- + o[ol++] = ">"; + break; + case 15: // end + if (sl && !s[sl - 1]) { // close = macro + o[ol - 1] == "\n" && (o[ol - 1] = ""), o[ol++] = ")", + o[ol++] = "\n", v = 1, sl -= 2; + } + break; + } + break; + case 2: // ========================== text parse_mode ========================== + switch (type) { + case 1: // -------- newline -------- + line_no++, last_line = pos; + if (ol != scope && ol != segment) // only output when not first + o[ol++] = "\\n"; + break; + case 2: // -------- misc -------- + if (ol == segment) // segment connectors + o[ol] = (ol++ == scope) ? "\"" : "+\""; + + o[ol++] = unesc_txt[tok] || tok; + break; + case 3: // word + if (ol == segment) + o[ol] = (ol++ == scope) ? "\"" : "+\""; + if(tok.charAt(tok.length-1)=='$'){ + o[ol++] = tok.slice(0,-1); + o[ol++] = tok = '$';// fix word$[xpath] + }else o[ol++] = tok; + break; + case 5: // -------- stringquotes -------- + if (ol == segment) + o[ol] = (ol++ == scope) ? "\"" : "+\""; + + o[ol++] = (tok == '"') ? "\\\"" : "'"; + break; + case 7: // -------- { -------- code mode + if (ol == segment){ + if (ol != scope ) + o[ol++] = "+"; + } + else + o[ol++] = "\"+", nesting || o_segs++; + s[sl++] = scope | 0x20000000, s[sl++] = o[ol++] = "{{", + nesting++, segment = scope = ol, parse_mode = 0; + break; + case 9: // -------- [ -------- xpath mode + last_model = null; // get xpath macro + if ((w = xpath_lut_text[last_tok]) && o[ol - 1] == last_tok) { + if (--ol - 1 == scope) + ol --; // remove first "" + } + else // only select c_xpathmode when nesting == 0 + w = xpath_macro[(nesting || scope != ol) ? 0 : c_xpathmode]; + + if (ol != scope) { + o[ol] = (ol++ == segment) ? "+" : (nesting || o_segs++, "\"+"); + + if (!nesting) + o_segs++; + } + + s[sl++] = scope | 0x20000000, s[sl++] = o[ol++] = w, + segment = scope = ol, nesting++, parse_mode = 3; + break; + case 15: // -------- end -------- + if (sl) + throw { + t: "Unclosed " + s[sl-1] + " found at end in textmode", + p: pos + }; + if (ol != scope && ol != segment) + o[ol++] = "\"", nesting || o_segs++; + break; + default: // -------- default -------- + if (ol == segment) + o[ol] = (ol++ == scope) ? "\"" : "+\""; + o[ol++] = tok; + } + break; + case 3: // ========================== xpath parse_mode ========================= + switch(type){ + case 0: // -------- whitespace -------- + if (ol != scope){ // strip initial spaces\l + if (ol == segment) + o[ol++] = "+\""; + o[ol++] = tok; + } + break; + case 1: // -------- newline -------- + line_no++, last_line = pos; + break; + case 2: // -------- misc -------- + if (tok == ":" && last_tok == ":" && !xpath_axes[w = o[ol - 2]] + && ((v = s[sl - 2]) >> 28) != 6) { // found model::xpath split + if (o[ol - 2] == '+"') // model is calculated + o[ol - 2] = o[ol - 1] = "", last_model = "#"; + else { + o[ol - 1] = '"'; + if (segment == scope) // model is normal name + last_model = o.slice(segment + 1, ol - 1).join(""); + else // model is calculated + last_model = "#"; + } + if (!(w = xpath_model[o[scope - 1]])) + throw { + t: "Invalid model found for: "+o[scope-1], + p: pos + }; + + o[scope - 1] = w, o[ol++] = ",", segment = scope = ol; + } + else { + if (tok == "," && (v = (s[sl - 2] >> 28)) <= 1) { // xpath is an array in code + ol = scope-1, u = str.slice(bt[scope] + 1, pos + 1); + // fix up stack to not be an xpath but an array + last_type = 9, parse_mode = v, o[ol++] = last_tok = "["; + s[sl - 2] = (s[sl - 2] & 0xfffffff) | (parse_mode << 28), + s[sl - 1] = last_tok, segment = scope = ol, nesting--; + + if (!nesting) + o_xpaths--; + if (u.length > 1) { // ignore [, optimized escaping + bts.push(str); // push str so str always is the string in replace + (str = u).replace(parserx, parser); // reparse it + str = bts.pop(); // pop it again + } + } + else { + if (ol == segment) + o[ol] = (ol++ == scope) ? "\"" : "+\""; + o[ol++] = unesc_lut[tok] || tok; + } + } + break; + case 3: // word + if (ol == segment) + o[ol] = (ol++ == scope) ? "\"" : "+\""; + if(tok.charAt(tok.length-1)=='$'){ + o[ol++] = tok.slice(0,-1); + o[ol++] = tok = '$';// fix word$[xpath] + }else o[ol++] = tok; + break + case 5: // -------- stringquotes -------- + if (ol == segment) + o[ol] = (ol++ == scope) ? "\"" : "+\""; + if (s[sl - 1] == "[") // strings only are used in [ ] + s[sl - 1] = tok; + else if(s[sl - 1] == tok) // close string + s[sl - 1] = "["; + + if (tok == '"') + o[ol++] = "\\"; + o[ol++] = tok; + break; + case 7: // -------- { -------- + if (ol == segment) { + if (ol != scope) + o[ol++] = "+''+"; + } + else + o[ol++] = "\"+"; + + s[sl++] = scope | 0x30000000, s[sl++] = o[ol++] = "{{", + nesting++, segment = scope = ol, parse_mode = 0; + + if (last_model && s[sl - 3] != xpath_lut_text["$"]) { + o_xpathpairs.push(last_model, "#"); + last_model = null, o_models++; + } + break; + case 9: // -------- [ -------- + // lets see if we are an xpath + if (s[sl - 1] == "'" || s[sl - 1] == '"' || + ((last_type != 3 || last_tok=='$') && last_tok != ")" && last_tok != "]") ) { + if (last_model) + o_xpathpairs.push(last_model, "#"), o_models++; + last_model = null; + + if ((w = xpath_lut_text[last_tok]) && o[ol - 1] == last_tok) + ol--; + else + w = xpath_macro[0]; + + if (ol == segment){ + if (ol != scope) + o[ol++] = "+"; + } + else o[ol++] = "\"+"; + + s[sl++] = scope | 0x30000000, s[sl++] = o[ol++] = w, nesting++, + segment = scope = ol, parse_mode = 3; + } + else { + if (ol == segment) + o[ol] = (ol++ == scope) ? "\"" : "+\""; + + s[sl++] = scope|0x60000000, s[sl++] = o[ol++] = "["; // keep track of [, abuse mode 6 + } + break; + case 10: // -------- ] -------- + sl--, parse_mode = (w = s[--sl]) >> 28, w = w & 0x0fffffff; + + if (parse_mode == 6){ // was part of [] internally to xpath, see above + if (s[sl + 1] != "[") + throw { + t:"In xpath, cannot close " + s[sl + 1] + " with " + tok, + p: pos + }; + if (ol == segment) + o[ol] = (ol++ == scope) ? "\"" : "+\""; + + o[ol++] = "]"; + parse_mode = 3; + } + else { + if (ol == scope ) { + if ((s[sl] >> 28) <= 1) // empty array in code + o[scope - 1] = "[", o[ol++] = "]"; + else // empty xpath elsewhere + o[scope - 1] = o[ol++] = "\"" ; + segment = ol; + } + else { + //if( s[sl+1] != '[' ) + // throw {t:"Unclosed string in xpath"+s[sl+1], p: pos}; + if (ol != segment) + o[ol++] = "\""; + if (segment == scope){ // we might have an xpath name + v = o.slice(segment + 1, ol - 1).join(""); + if (c_injectself && o[scope - 1] != "," // inject self + && v != (u = v.replace(selfrx, "$1self::")) + && s[sl + 1] != xpath_lut_text["$"]) { + o[scope+1] = v = u; + for (u = scope + 2; u < ol - 1; u++) + o[u] = ""; + } + } + else { + if ((u = o[scope - 1]) != ",") { + v = "#"; + if (c_injectself)// inject dyn self if dyn xpath + o[scope - 1] = u + "_injself(", o[ol++] = ")"; + } + else + v = ""; + } + if (s[sl + 1] != xpath_lut_text["$"] && v) { + o_xpathpairs.push(last_model, v); // only store if not _lng + if (last_model) + o_models++; + } + o[ol++] = ") ", segment = ol; // close xpath with ') ' marker + //logw("CLOSING XPATH"+o.join('#')+nesting); + if (parse_mode == 7) // attribute assign in xml mode + o[ol++] = "+\"\\\"", parse_mode = 4; + } + // lets output an xpath if we werent a language symbol + nesting--, last_model = null; + if (!nesting) + o_segs++, o_xpaths++; + } + scope = w; + break; + case 11: // -------- ( -------- + if (ol == segment) + o[ol] = (ol++ == scope) ? "\"" : "+\""; + s[sl++] = scope | 0x30000000, // keep track of () in xpath + s[sl++] = o[ol++] = "(";//, last_model = null; + break; + case 12: // -------- ) -------- + if (ol == segment) + o[ol] = (ol++ == scope) ? "\"" : "+\""; + + if (type_close[v = s[--sl]] != (o[ol++] = tok)) + throw { + t:"Cannot close " + v + " with " + tok, + p: pos + }; + + scope = s[--sl] & 0xfffffff; + break; + case 15: // -------- end -------- + throw { + t: "Unexpected end whilst parsing xpath", + p: pos + }; + break; + default: // -------- default -------- + if (ol == segment) + o[ol] = (ol++ == scope) ? "\"" : "+\""; + o[ol++] = tok; + break; + } + break; + case 4: // =========================== xml parse_mode ========================== + switch (type) {// stack: '<'sl+4,outside=0, ''sl-2,outside=1 '/>'sl-4,outside=1 + case 0: // -------- whitespace -------- + if (ol == segment) + o[ol++] = "+\""; + + o[ol++] = " ", last_type = 0; + break; + case 1: // -------- newline -------- + if (ol == segment) + o[ol++] = "+\""; + + line_no++, last_line = pos, o[ol++] = "\\n", last_type = 1; + break; + case 2: // -------- misc -------- + if (ol == segment) + o[ol++] = "+\""; + if (tok == "/" && last_tok == "<") { + sl -= 4; // -------- + if (ol == segment) + o[ol++] = "+\""; + + o[ol++] = tok; + if (last_tok != "<") { + if (last_tok == "/") { + sl -= 4; // self close tag /> drops stack -4 + if (s[sl + 2]) + throw { + t: "Unexpected / whilst parsing xml", + p: pos + } + if (o[ol - 3] == "<") // remove empty from output + ol -= 2, o[ol - 1] = ""; + } + else + sl -= 2; // nets stackdepth of 2 + if (s[sl]) { // end of xml mode + nesting--, o[ol++] = "\"", scope = s[sl], segment = ol, + parse_mode = scope >> 28, scope = scope & 0x0fffffff; + } + else + s[sl - 1] = 1; // we are outside a tag, flag it on the stack + } + else // remove empty <> from output + ol--, o[ol - 1] = ""; + break; + case 9: // -------- [ -------- xpath mode + last_model = null; + + if (last_tok == "!" && o[ol - 2] == "<" && !s[sl - 1]) { // CDATA mode + o[ol++] = tok, s[sl++] = scope | (parse_mode << 28); + s[sl++] = "]]>", scope = segment = ol - 1; + nesting++, parse_mode = 5; + } + else { + if (s[sl - 1]) { // we are outside a tag + if((v = xpath_lut_node[last_tok])) + ol --; + else + v = xpath_macro[c_elemxpath]; + + s[sl++] = scope | 0x40000000 + } + else { + s[sl++] = scope | 0x40000000 + if ((v = xpath_lut_attr[last_tok])) { + ol--; + if (o[ol - 1] == "=") + last_tok = "="; + } + else + v = xpath_macro[last_ns ? c_statexpath : 8]; + + if (last_tok == "=")//0x7 flags xpath-in-missing-quotes + o[ol++] = "\\\"", s[sl - 1] = scope | 0x70000000; + } + o[ol] = (ol++ == segment) ? "+''+" : "\"+"; + nesting++, s[sl++] = o[ol++] = v, + segment = scope = ol, parse_mode = 3; + } + break; + case 7: // -------- { -------- code mode + if ( !s[sl - 1] && last_tok == "=") // 0x7 flags code-in-missing-quotes + o[ol++] = "\\\"", s[sl++] = scope | 0x70000000; + else + s[sl++] = scope | 0x40000000 + + o[ol] = (ol++ == segment) ? "+''+" : "\"+"; + s[sl++] = o[ol++] = "{{", nesting++; + segment = scope = ol, parse_mode = 0; + break; + default: + if (ol == segment) + o[ol++] = "+\""; + o[ol++] = tok; + break; + case 15: // -------- end -------- + throw { + t: "Unexpected end whilst parsing xml", + p: pos + }; + break; + }break + case 5: // ========================== string parse_mode ======================== + switch (type) { + case 1: // -------- newline -------- + line_no++, last_line = pos; + if (ol == segment) + o[ol] = (ol++ == scope) ? "\"" : "+\""; + o[ol++] = "\\n"; + break; + case 2: // -------- misc -------- + if (tok == "/" && s[sl - 1] == "/") { // regexp closing character + o[ol++] = "/", scope = s[sl -= 2], segment = ol, + parse_mode = scope >> 28, + scope = scope & 0x0fffffff, nesting--; + } + else { + if (ol == segment) + o[ol] = (ol++ == scope) ? "\"" : "+\""; + o[ol++] = (s[sl - 1] != "/" && unesc_str[tok]) || tok; + } + break; + case 3: // word + if (ol == segment) + o[ol] = (ol++ == scope) ? "" : "+\""; + if(tok.charAt(tok.length-1)=='$'){ + o[ol++] = tok.slice(0,-1); + o[ol++] = tok = '$';// fix word$[xpath] + }else o[ol++] = tok; + break + case 5: // -------- stringquotes -------- + if (s[sl - 1] == tok) { // closed by matching quote + if (scope != segment) // string is segmented, output ) + o[ol] = (ol++ != segment) ? (tok + ")") : ")"; + else + o[ol++] = tok; // else just close + scope = s[sl -= 2], segment = ol, parse_mode = scope >> 28; + scope = scope & 0x0fffffff, nesting--; + } + else { + if (ol == segment) + o[ol] = (ol++ == scope) ? "\"" : "+\""; + o[ol++] = tok == '"' ? "\\\"" : tok; + } + break; + case 6: // -------- default -------- + if (s[sl - 1] == "/" && tok == "*/") { // caught faux comment in regexp /a*/, is close + o[ol++] = "*/", scope = s[sl -= 2], segment = ol, + parse_mode = scope >> 28, scope = scope & 0x0fffffff, nesting--; + } + else { + if (ol == segment) + o[ol] = (ol++ == scope) ? "" : "+\""; + o[ol++] = tok; + } + break; + case 7: // -------- { -------- code mode + if (s[sl - 1] != "'" && s[sl - 1] != "/") { + if (s[sl - 1] == '"') + o[scope] = '("'; + if (ol == segment) { + if (ol != scope) + o[ol++] = "+"; + } + else + o[ol++] = "\"+"; + s[sl++] = scope | 0x50000000, o[ol++] = s[sl++] = "{{", + nesting++, segment = scope = ol, parse_mode = 0; + } + else + o[ol++] = tok; + break; + case 9: // -------- [ -------- xpath mode + if (s[sl - 1] != "'" && s[sl - 1] != "/" // ignore in '' and CDATA[, else xpath + && (s[sl - 1] == '"' && (o[scope] = '("') || ol != scope + 2 + || last_tok != "CDATA") ) { + last_model = null; + if ((w = xpath_lut_text[last_tok]) && o[ol - 1] == last_tok) + ol--; + else + w = xpath_macro[0] + + if (ol != scope) + o[ol] = (ol++ == segment) ? "+" : "\"+"; + + s[sl++] = scope | 0x50000000, s[sl++] = o[ol++] = w, + segment = scope = ol, nesting++, parse_mode = 3; + } + else + o[ol++] = tok; + break; + case 14: // -------- > -------- + if (ol == segment) + o[ol] = (ol++ == scope) ? "" : "+\""; + o[ol++] = tok; + + if (s[sl - 1] == "]]>" && last_tok == "]" && o[ol - 3]=="]") { // check if CDATA close + scope = s[sl -= 2], parse_mode = scope >> 28; + scope = scope & 0x0fffffff, nesting--; + sl -= 4; // close the tag since we came from XML mode + if (s[sl]) // was last tag, jump up the stack one more. + nesting--, o[ol++] = "\"", scope = s[sl], segment = ol, + parse_mode = scope >> 28, scope = scope & 0x0fffffff; + else + s[sl - 1] = 1; + } + break; + case 15: // -------- end -------- + throw { + t: "Unexpected end whilst parsing string", + p: pos + }; + break; + default: // -------- default -------- + if (ol == segment) + o[ol] = (ol++ == scope) ? "" : "+\""; + o[ol++] = tok; + break; + } + break; + case 6: // ========================= comment parse_mode ======================== + switch (type) { + case 1: // -------- newline -------- + line_no++, last_line = pos; + if (start_tok == "//") + parse_mode = last_cmt_mode, + tok = last_tok = last_cmt_tok, + type = last_type = last_cmt_type; + break; + case 6: // -------- comment -------- + if ((start_tok == "/*" && tok == "*/") + || (start_tok == "")) { + parse_mode = last_cmt_mode, + tok = last_tok = last_cmt_tok, + type = last_type = last_cmt_type; + } + break; + case 15: // -------- end -------- + if(start_tok != "//"){ + throw { + t: "Unexpected end whilst parsing comment", + p: pos + } + } else { + parse_mode = last_cmt_mode, + tok = last_tok = last_cmt_tok, + type = last_type = last_cmt_type; + if (sl && !s[sl - 1]) { // close = macro + o[ol - 1] == "\n" && (o[ol - 1] = ""), o[ol++] = ")", + o[ol++] = "\n", v = 1, sl -= 2; + } + }; + break; + } + break; + } + if (type > 1) + last_tok = tok, last_type = type; + } + + this.lastCode = function(){ + if (typeof(o) == "object") + return o.join(""); + return o; + }; + + function handleError(e, last_line, part, linenr){ + // TODO: make a proper APF exception with this information: + if (e.t) { + throw new Error(apf.formatErrorString(0, null, + "Parsing live markup source", + "Error whilst parsing: " + e.t + " on line:"+ line_no + + " col:" + (e.p - last_line - 2) + + (part ? (" part: " + part) : "") + "\n" + str)); + } + else { + throw new Error(apf.formatErrorString(0, null, + "Compiling live markup function on line " + linenr, + "Error whilst compiling: " + e.message + //+ "\nStack Trace:\n" + e.stack + + "\nInput:\n" + str + + "\nGenerated:\n" + apf.lm.lastCode())); + } + } + + /** + * description of the method. + * Remarks: + * function{type:1,xpaths:[ model,name], props: ['obj.propname','obj2.otherpropname'], asyncs=1} + * this is a normal compiled function with extra properties + * if the xpath model and/or name is '#' it means it is a runtime calculated modelname or xpath. + * obj{type:2, str:str} it was single string by cfg option !alwayscode + * obj{type:3, xpaths:[ model, name ] } it was a single xpath by cfg simplexpath + * + * @param {String} str the code to compile + * @param {Object} options + * Properties: + * {Boolean} withopt creates with(_w){ code using an options block. (reqd for precall) + * {Boolean} precall wraps 1 async call into precallstore. call with _w._pc = 1 to precall, second time to execute. + * {Boolean} alwayscb always call callback function, even if not async + * {Boolean} nostring even generate code for a simple string + * {Number} xpathmode default type of root level xpath in code mode + * Possible values: + * 0: value + * 1: value with createnode + * 2: node + * 3: node with createnode + * 4: nodes + * 5: xpathobj returns a {model:model,xpath:xpath} object from xpaths + * {Boolean} parsecode start in codemode. if 0 its textmode. + * {Boolean} nostate dont' use _valst macro on [xpath] in namespaced xml. + * {Boolean} liveedit use the _valed macro for [xpath] in namespaced xml. + * {Boolean} langedit use of language items in namespaced xml text. + * {Boolean} injectself injects self:: to suitable xpaths + * {Boolean} event its an event thats being compiled, results in no returnvalue for this function. + * and the first argument is now an 'e' for the event object. + * {Boolean} funcglobal all functions defined in LM are made global + + * + * @return {Function} returns a function with extra properties + * Properties: + * {Number} type description + * Possible values: + * 1 Function return type + * 2 Parsed data is a pure string + * 3 Function return type, but its a single xpath + * 4 Function return type, but single propxs + * {Array} xpaths array of [model,xpath, model,xpath] pairs if model + * or xpath is '#', its dynamic if model is null its a local xpath + * {Number} models number of models + * {Array} props description + * {Number} asyncs description + * {String] str optional, returned with type 2 + */ + var cache = {}, + emptyCfg = {}; + this.resetCache = function(){ + cache = {}; + }; + var lmcache_rx = /^\s*~~(c\d+)~~/; + this.compile = function(istr, cfg){ + if (!cfg) + cfg = emptyCfg; + if(istr == null || !istr.length){ + return (cfg.nostring || cfg.event)?function(){return istr}:{ + type: 2, + str :istr + }; + } + // lets see if we need to fetch precompiled cachemarker + var c, f, is_single_prop; + if(istr.charAt(0)=="~" && (c=istr.match(lmcache_rx))){ + if(c=apf.lm_exec[c[1]]) return c; + alert("ERROR, undefined live markup cache marker found:"+istr); + return {type:2,str:istr}; + } + + var key = (cfg.xpathmode | (cfg.withopt && 0x10) | (cfg.precall && 0x20) + | (cfg.alwayscb && 0x40) | (cfg.nostring && 0x80) | (cfg.parsecode && 0x100) + | (cfg.nostate && 0x200) | (cfg.liveedit && 0x400)| (cfg.langedit && 0x800) + | (cfg.injectself && 0x1000) | (cfg.event && 0x2000) | (cfg.funcglobal && 0x4000)) + istr; + + if (c = cache[key]) + return c; + + + c_injectself = cfg.injectself, c_xpathmode = cfg.xpathmode||0, + c_statexpath = cfg.nostate ? 0 : 6, c_elemxpath = 0; + c_export = cfg.funcglobal?"self":(cfg.withopt?"_w":null); + c_process_async = !cfg.event; + + xpath_macro.edit = cfg.liveedit ? "_argwrap(_n," : "_argwrap(_n,";//"_val(_n,"; + macro_o.edit = cfg.liveedit ? macro_o._editlm : macro_o._editnormal; + + xpath_lut_node = cfg.langedit ? xpath_lut_node_langedit : xpath_lut_node_normal; + + o_props = {}, o_xpathpairs = [], s = [], o = ["","","",""], str = istr, + str_len = str.length; + ol = scope = segment = o.length, + o_segs = o_xpaths = o_asyncs = o_models = nesting = line_no = last_type = last_line = 0; + + if (cfg.parsecode) { + parse_mode = 0, sl = 2, s[0] = ol, s[1] = "{{", last_tok = "{", + cf_mode_output = cfg.event ? "" : (c_xpathmode <= 1 ? cf_str_output : cf_obj_output); + } + else + parse_mode = 2, sl = last_tok = 0, cf_mode_output = cf_str_output; + + if (cfg.nothrow) { + str.replace(parserx, parser); + } + else { + try { + str.replace(parserx, parser); + } + catch(e) { + handleError(e, last_line); + return null; + } + } + + if (cfg.parsecode) { + if (nesting || s[sl - 1].length == 1) + handleError({ + t: "Unclosed " + s[sl-1] + " found at end in codemode", + p: str_len + },last_line); + if (segment!=ol) + o_segs++ + }else if( (ol==7 || ol==8) && o_segs == 1){ + is_single_prop = 0; + for(c in o_props)is_single_prop++; + if(is_single_prop!=1)is_single_prop = 0; + } + if ((!cfg.nostring && !cfg.event)&& (parse_mode == 2 && segment == 4 || ol == 4)) { + return { + type: 2, + str : o.slice(5, -1).join("").replace(/\\n/g, "\n").replace(/\\"/g, '"') + }; // string only + } + if (o_asyncs || cfg.alwayscb) { + + if (cfg.event) { // event + if (parse_mode == 1) + o[3] = ""; + o[ol++] = cc_o_blk_ce; + } + else if (c_xpathmode) { // object return + if (parse_mode == 1) { + o[3] = (o[3] != cf_block_o) ? cc_o_blk_o : cc_o_blk_ob, + o[ol++] = cc_o_blk_cb; + } + else + o[3] = cc_o_cb_o, o[ol++] = cc_o_cb_c; + } + else { // value return + if (parse_mode == 1) + o[3] = (o[3] != cf_block_o) ? cc_v_blk_o : cc_v_blk_ob, + o[ol++] = cc_v_blk_cb; + else + o[3] = cc_v_cb_o, o[ol++] = cc_v_cb_c; + } + + if (o_asyncs) { + // for parse_mode == 1 we can squeeze in before [3] and cb close + // else we put var _r= in 3 and put our ending last and put + // the cb at the end + if(parse_mode==1){ + if (cfg.precall) + o[2] = cc_pc_o, o[ol-1] = cc_pc_c + o[ol-1]; + else + o[2] = cc_async_o, o[ol-1] = cc_async_c + o[ol-1]; + }else{ + o[ol++] = o[3] + '_r' + o[ol-2]; + if (cfg.precall) + o[2] = cc_pc_o, o[3] = cc_o_blk_o, o[ol-2] = cc_pc_c; + else + o[2] = cc_async_o, o[3] = cc_o_blk_o, o[ol-2] = cc_async_c; + } + } + + if (cfg.withopt) + o[1] = cc_opt_o, o[ol++] = cc_opt_c; + + o[0] = cfg.event + ? cc_fe_async_o + : ((c_xpathmode == 1 || c_xpathmode == 3) ? cc_fc_async_o : cc_f_async_o); + o[ol++] = cc_f_c; + } + else { + if (cfg.event) { // event + if (parse_mode == 1) + o[3] = ""; + } + else if(c_xpathmode) { // object return + if (parse_mode == 1) { + o[3] = (o[3] != cf_block_o) ? cc_o_blk_o : cc_o_blk_ob, + o[ol++] = cc_o_blk_c; + } + else + o[3] = cc_o_ret_o, o[ol++] = cc_o_ret_c; + } + else { // value return + if (parse_mode == 1) { + o[3] = (o[3] != cf_block_o) ? cc_v_blk_o : cc_v_blk_ob, + o[ol++] = cc_v_blk_c; + } + else + o[3] = cc_v_ret_o, o[ol++] = cc_v_ret_c; + } + if (cfg.withopt) + o[2] = cc_opt_o, o[ol++] = cc_opt_c; + + o[0] = cfg.event + ? (cfg.withopt ? cc_fe_opt_o : cc_fe_o) + : (cfg.withopt + ? ((c_xpathmode == 1 || c_xpathmode == 3) ? cc_fc_opt_o : cc_f_opt_o) + : ((c_xpathmode == 1 || c_xpathmode == 3) ? cc_fc_o : cc_f_o)); + o[ol++] = cc_f_c; + } + + if (cfg.nothrow) { + f = apf.lm_exec.compile(o.join("")); + } + else { + try { + f = apf.lm_exec.compile(o.join("")); + } + catch(e){ + if (!apf.isIE) { + var oErr = window.onerror; + window.onerror = function(x,y,line){ + window.onerror = oErr; + handleError(e, last_line, null, line); + return true; + } + apf.include("", "", null, o.join("")); + window.onerror = oErr; + } + else { + handleError(e,last_line); + } + return null; + } + } + f.type = (o_segs == 1 && o_xpaths == 1) ? 3 : (is_single_prop?4:1); + f.xpaths = o_xpathpairs, f.models = o_models, + f.props = o_props, f.asyncs = o_asyncs; + + cache[key] = f; + return f; + }; + + /** + * description of the method. + * Remarks: + * @param {String} str the code to compile + * @param {Object} options + * Properties: + * {Boolean} node tries to return a node, used as a dual-compile with 'normal mode' + * + * @return {Function} returns a function with extra properties + * Properties: + * {Number} type description + * Possible values: + * 1 Function return type + * 2 Parsed data is a pure string + * 3 Function return type, but its a single xpath + * {Array} xpaths array of [model,xpath, model,xpath] pairs if model + * or xpath is '#', its dynamic if model is null its a local xpath + * {Number} models number of models + * {Array} props description + * {Number} asyncs description + * {String] str optional, returned with type 2 + */ + + this.compileMatch = function(strarray, cfg){ + if (!cfg) + cfg = emptyCfg; + + o_props = {}, o_xpathpairs = [], o = [cc_f_match_o, cc_m_o], s = [], + nesting = 0, ol = o.length, xpath_lut_node = xpath_lut_node_normal; + + for (var st, ob, i = 0, j = strarray.length; i < j; i += 2) { + if (str = strarray[i]) { + str_len = s.length, c_xpathmode = 2; + if (i) + o[ol++] = cc_m_brk; + o[ol++] = ""; + s[0] = ob = ol = scope = segment = o.length, cf_mode_output = cf_obj_output; + line_no = last_type = o_segs = o_xpaths = o_asyncs = parse_mode = last_line = 0; + sl = 2, s[1] = "{{", last_tok = "{"; + c_injectself = 1; + + if (cfg.nothrow) { + str.replace(parserx, parser); + } + else { + try { + str.replace(parserx, parser); + } + catch(e) { + handleError(e,last_line); + return null; + } + } + + if (nesting || s[sl - 1].length == 1) + handleError({ + t: "Unclosed " + s[sl - 1] + " found at end in codemode", + p: str_len + }); + + if (o_asyncs) + handleError({t:"Asynchronous calls not supported in match/value"}); + + if (parse_mode == 1) { // block mode + o[ob - 1] = (o[ob - 1] != cf_block_o) ? cf_mode_output : "", + o[ol++] = cc_m_m_blk; + } + else // value mode + o[ob-1] = cc_m_m_value_o, o[ol++] = cc_m_m_value_c; + } + if (str = strarray[i + 1]) { + str_len = s.length; + if(!strarray[i] && i) + o[ol++] = cc_m_brk; + o[ol++] = ""; + ob = ol = scope = segment = o.length, cf_mode_output = cf_str_output; + c_xpathmode = c_injectself = last_tok = sl = line_no = o_segs = o_xpaths = + last_type = o_asyncs = last_line = 0; + if(cfg.node) + c_xpathmode = 2; + parse_mode = 2, c_injectself = 0; + + if (cfg.nothrow) { + str.replace(parserx, parser); + } + else { + try { + str.replace(parserx, parser); + } + catch(e) { + handleError(e,last_line); + return null; + } + } + + if (o_asyncs) + handleError({t:"Asynchronous calls not supported in match/value"}); + + if(cfg.node){ + if (parse_mode == 2 && segment == ob || ol == ob) + o[ob-1] = cc_m_n_string; + else + o[ob-1] = cc_m_n_o, o[ol++] = cc_m_n_c; + }else{ + if (parse_mode == 2 && segment == ob || ol == ob) + o[ob-1] = cc_m_v_string; + else + o[ob-1] = cc_m_v_o, o[ol++] = cc_m_v_c; + } + + if (strarray[i]) + o[ol++] = cc_m_c; + else + break; + } + else { + if (!strarray[i]) + handleError({t:"Both match and value are empty"}); + + if(cfg.node) + o[ol++] = cc_m_n_ret; + else + o[ol++] = cc_m_v_ret; + + c_xpathmode = 2; + + o[ol++] = cc_m_c; + } + } + o[ol++] = cc_f_c; + + var f; + if (cfg.nothrow) { + f = apf.lm_exec.compile(o.join("")); + } + else { + try{ + f = apf.lm_exec.compile(o.join("")); + } + catch(e){ + handleError(e,last_line); + return null; + } + } + + f.type = 1, f.xpaths = o_xpathpairs, + f.props = o_props, f.asyncs = o_asyncs; + return f; + }; + + this.setWarnLevel = function(lvl){ + apf.lm_exec.setWarnLevel(lvl); + }; + + this.parseExpression = function(istr, cfg){ + if (!cfg) + cfg = emptyCfg; + + o_props = {}, o_xpathpairs = [], o = [], s = [], + nesting = 0, xpath_lut_node = xpath_lut_node_normal; + str = istr, str_len = str.length; + ob = ol = scope = segment = o.length, cf_mode_output = cf_str_output; + c_xpathmode = c_injectself = last_tok = sl = line_no = o_segs = o_xpaths = + last_type = o_asyncs = last_line = 0; + parse_mode = 2; + + if (cfg.nothrow) { + str.replace(parserx, parser); + } + else { + try { + str.replace(parserx, parser); + } + catch(e) { + handleError(e,last_line); + return null; + } + } + return o.join(''); + } + + +})(); + +// apf lm_exec makes sure there is no scope pollution for eval'ed live markup. +apf.lm_exec = new (function(){ + + var wlvl = 1; // 0: no warnings 1: language/models missing, 2:nodes missing, 3:all failed xpaths + + //warning functions + this.setWarnLevel = function(lvl){ + wlvl = lvl; + }; + + function wxpath(x, t){ + apf.console.warn("Live Markup warning in " + t + ", no results for xpath: '" + x + "'"); + } + + function wnode(x, t){ + apf.console.warn("Live Markup warning in " + t + ", xpath on null node: '" + x + "'"); + } + + function wmodel(m, x, t){ + apf.console.log("Live Markup warning in " + t + ", xpath on empty model: '" + m + "' xpath: '" + x + "'"); + } + + function wlang(x, t){ + apf.console.log("Live Markup warning in " + t + ", language symbol not found: '" + x + "'"); + } + + // xml parse function used by all livemarkup objects + function xmlParse(str){ + var n = apf.getXmlDom("<_apflmlist_>" + str + ""); + if (!n || !(n = n.documentElement)) + return null; + return (n.firstChild == n.lastChild) ? n.firstChild : n; + } + + // value of node by xpath + function __val(n, x){ + if (!n) + return ("") + return (n = (!n.nodeType && n || (n = n.selectSingleNode(x)) //!= 1 + && (n.nodeType != 1 && n || (n = n.firstChild) && n.nodeType!=1 && n))) + && n.nodeValue || (""); + } + + var __valattrrx = /(["'])/g; + function __valattrrp(m,a){ + return m=='"'?""":"'"; + } + function __valattr(n, x){ + if (!n) + return ("") + return (n = (n.nodeType != 1 && n || (n = n.selectSingleNode(x)) + && (n.nodeType != 1 && n || (n = n.firstChild) && n.nodeType!=1 && n))) + && n.nodeValue.replace(__valattrrx,__valattrrp) || (""); + } + + + // value of model node by xpath + function __valm(m, x){ + var n; + if (!m || !(n = (m.charAt && ((m.charAt(0) == "<" && xmlParse(m)) + || ((n = apf.nameserver.lookup.model[m]) && n.data))) + || (m.$isModel ? m.data : (m.charAt ? 0 : m)))) + return (""); + return (n = (n.nodeType != 1 && n || (n = n.selectSingleNode(x)) + && (n.nodeType != 1 && n || (n = n.firstChild) && n.nodeType!=1 && n))) + && n.nodeValue || (""); + } + + function __nod(n, x){ // node by xpath + return n ? n.selectSingleNode(x) : (null); + } + + function _nods(n, x){ // array of nodes by xpath + return n ? n.selectNodes(x) : ([]); + } + + function __nodm(m, x){ // node of model by xpath + var n; + if (!m || !(n = (m.charAt && ((m.charAt(0) == "<" && xmlParse(m)) + || ((n = apf.nameserver.lookup.model[m]) && n.data))) + || (m.$isModel ? m.data : (m.charAt ? 0 : m)))) + return (null); + + return n.selectSingleNode(x); + } + + function _nodsm(m, x){ // array of nodes from model by xpath + var n; + if (!m || !(n = (m.charAt && ((m.charAt(0) == "<" && xmlParse(m)) + || ((n = apf.nameserver.lookup.model[m]) && n.data))) + || (m.$isModel ? m.data : (m.charAt ? 0 : m)))) + return ([]); + + return n.selectNodes(x); + } + + function __cnt(n, x){ // count nodes by xpath + return n ? n.selectNodes(x).length:(0); + } + + function __cntm(m, x){ // count nodes from model by xpath + var n; + if (!m || !(n = (m.charAt && ((m.charAt(0) == "<" && xmlParse(m)) + || ((n = apf.nameserver.lookup.model[m]) && n.data))) + || (m.$isModel ? m.data : (m.charAt ? 0 : m)))) + return (0); + + return n.selectNodes(x).length; + } + + function _xpt(n, x){ // return the query wrapped in an object + return { + xpath : x, + toString: function(){ + return "LM Xpath object: " + this.x + } + }; + } + + function _xptm(m, x){ // return the query with model wrapped in an object + if (m && !m.$isModel) { + var node = m; + m = apf.xmldb.findModel(m); + x = apf.xmlToXpath(node, m.data) + "/" + x; + } + + return { + model: m, + xpath: x, + toString: function(){ + return "LM Xpath object with model: " + this.x + } + }; + } + + //----- the following functions are combined model and normal mode ------ + + function _xml(n, m, x){ // serialize node by xpath via .xml + if(n) x = m; + else if(!m || !(n=(m.charAt && ((m.charAt(0)=="<" && xmlParse(m)) || + ((n = apf.nameserver.lookup.model[m]) && n.data))) || + (m.$isModel?m.data:(m.charAt?0:m)))) + return (""); + + return (n && (n = n.selectSingleNode(x))) && n.xml || + (""); + } + + function _xmls(n, m, x){ // serialize nodes by xpath with .xml concatenated + if(n) x = m; + else if(!m || !(n=(m.charAt && ((m.charAt(0)=="<" && xmlParse(m)) || + ((n = apf.nameserver.lookup.model[m]) && n.data))) || + (m.$isModel?m.data:(m.charAt?0:m)))) + return (""); + for(var i = 0,j = ((n=n.selectNodes(x))).length,o = [];i' + ((n?__val(n,m):__valm(m,x)) || " ") + ''; + } + +// function _edit(n, opts){ +// return '' + ((n?__val(n,m):__valm(m,x)) || " ") + ''; +// } + + function _argwrap(n,x){ + return [n,x]; + } + + function _argwrapm(m,x){ + return [0,m,x]; + } + + function _valedx(editMode, args, opt){ // wrap a value with editable div + args[3] = opt; + args[4] = editMode; + return _valed.apply(this, args); + } + + function _valed(n, m, x, options, editMode){ // wrap a value with editable div + var res = (n?__val(n,m):__valm(m,x)); + + if (options && options.multiline && options.editor != "richtext") + res = res.replace(/\n/g, "
"); + + if (editMode !== false) { + var value = res || options && options.initial || " "; + if (!options || !options.richtext) + value = apf.htmlentities(value); + if (options && options.multiline) + value = value + .replace(/<br ?\/?>/g, "
") + .replace(/<(\/?div)>/g, "<$1>"); + + return '' + value + + ''; + } + else { + return res; + } + } + + var selfrx = /(^|\|)(?!\@|text\(\)|\.\.|[\w\-\:]+?\:\:)/g; // inject self regexp + + function _injself(s){ // self inject helper func + return s.charAt?s.replace(selfrx, "$1self::"):s; + } + + apf.$lmx = null; + + function _async(_n,_c,_a,_w,_f,_this,obj,func,args){ // Async handling + var i = _a.i, v; + + if(!_a.ret)_a.ret = []; + + if (_a[i]) + return _a.ret[i]; + + _a[i] = true; // flag this ID so args dont get computed again + + if (!obj.exec) + return _a.ret[i]=(func)?obj[func].apply(obj,args):obj.apply(obj,args); + + var cb = function(data, state, extra){ + if (_w) + delete _w._pc; + + if (state != apf.SUCCESS){ + _c(null, state, extra); + } + else{ + apf.$lmx = extra; + _a.ret[i] = data; + + if (_w) + _f.call(_this,_n,_c,_w,_a); + else + _f.call(_this,_n,_c,_a); + } + }; + + if(_w && _w._pc){ + _w._pc = { + obj : obj, + func : func, + args : args, + message : obj.createMessage && obj.createMessage(func, args), + _c : _c, + cb : cb + }; + }else{ + obj.exec(func, args, cb); + } + throw({ + x:1 + }); + } + + function _precall(_w){ // precall + var o; + if (typeof(o = _w._pc) != "object" || !o) + return; + + o.obj.exec(o.func, o.args, o.cb, {message: o.message}); + + throw({x:1}); + } + + var _clut = apf.color?apf.color.colorshex:{}, _cparse = /^(rgb|hsv|hsb)\(\s+(\d+)\s+,\s+(\d+)\s+,\s+(\d+)\)/ + + function sort(set, xpath, options){ + var s = new apf.Sort(); + options = options || {}; + if(!xpath.charAt)xpath = ""; + if(xpath.charAt(0)=='@'){ + xpath = xpath.slice(1); + options.getValue = function(n){ + return n.getAttribute(xpath); + } + }else{ + options.getValue = function(n){ + return apf.queryValue(n,xpath); + } + } + s.set(options); + return s.apply(apf.getArrayFromNodelist(set)); + } + + function _cthex(c){ + var t; + if((t=typeof(c))=='string'){ + if(c.indexOf('#')==0){ + if(c.length==7) return parseInt(c.slice(-1),16); + return parseInt(c.slice(-1),16); // compute repeat + } + if(t = _clut[a])return t; + if(c=c.match(_cparse)){ + if((t=c[1]) == 'rgb'){ + return (((t=c[2])<0?0:(t>255?255:parseInt(t)))<<16)+ + (((t=c[3])<0?0:(t>255?255:parseInt(t)))<<8)+ + (((t=c[4])<0?0:(t>255?255:parseInt(t)))); + } else { // hsv + var h=parseFloat(c[2]),s=parseFloat(c[3]),v=parseFloat(c[4]), + i,m=v*(1-s),n=v*(1-s*((i=floor(((h<0?-h:h)%1)*6))?h-i:1-(h-i))); + switch(i){ + case 6:case 0: return ((v&0xff)<<16)+((n&0xff)<<8)+(m&0xff); + case 1: return ((n&0xff)<<16)+((v&0xff)<<8)+(m&0xff); + case 2: return ((m&0xff)<<16)+((v&0xff)<<8)+(n&0xff); + case 3: return ((m&0xff)<<16)+((n&0xff)<<8)+(v&0xff); + case 4: return ((n&0xff)<<16)+((m&0xff)<<8)+(v&0xff); + default:case 5: return ((v&0xff)<<16)+((m&0xff)<<8)+(n&0xff); + } + } + } + } else if(t=='number')return t; + return null; + } + + function lin(f, a, b){ + var fa = parseFloat(a), fb = parseFloat(b), fm = 1-(f = f<0?0:(f>1?1:f)); + if(fa!=a || fb!=b) + return (((fa=_cthex(a))&0xff0000)*f+((fb=_cthex(b))&0xff0000)*fm)&0xff0000| + ((fa&0xff00)*f+(fb&0xff00)*fm)&0xff00 | + ((fa&0xff)*f+(fb&0xff)*fm)&0xff; + return f*fa+fm*fb; + } + + var abs = Math.abs, acos = Math.acos, asin = Math.asin, + atan = Math.atan, atan2 = Math.atan2, ceil = Math.ceil, + cos = Math.cos, exp = Math.exp, floor = Math.floor, + log = Math.log, max = Math.max, min = Math.min, + pow = Math.pow, random = Math.random, round = Math.round, + sin = Math.sin, sqrt = Math.sqrt, tan = Math.tan, linear = lin; + + function tsin(x){ return 0.5*sin(x)+0.5;} + function tcos(x){ return 0.5*cos(x)+0.5;} + function usin(x){ return 0.5-0.5*sin(x);} + function ucos(x){ return 0.5-0.5*cos(x);} + function snap(a,b){ return round(a/b)*b; } + function clamp(a,b,c){ return ac?c:a); } + + this.compile = function(code){ + // up-scope much used functions + var _ret = __ret, _val = __val,_valm = __valm, _nod = __nod, + _nodm = __nodm, _cnt = __cnt, _cntm = __cntm, _lng = __lng, _valattr = __valattr; + + eval(code); + + return _f; + } + + this.compileWith = function(code, withs){ + // up-scope much used functions + var _ret = __ret, _val = __val,_valm = __valm, _nod = __nod, + _nodm = __nodm, _cnt = __cnt, _cntm = __cntm, _lng = __lng, _valattr = __valattr; + + eval(code); + + return _f; + } + + var LMBEGINCACHE; + /*LIVEMARKUP BEGIN CACHE + var _ret = __ret, _val = __val,_valm = __valm, _nod = __nod, + _nodm = __nodm, _cnt = __cnt, _cntm = __cntm, _lng = __lng, _valattr = __valattr; + this.c342 = function(_n,_a,_w){ + ..cached LM function.. + } + this.c342.type = 2; + this.c342.xpaths = {...}; + this.c342.props = {...}; + this.c723 = function(....){ + + } + // replace + d.replace(/var_LMBEGINCACHE;[\s\S]*var_LMBEGINCACHE;/,"code"); + _async(_n,_c,_a,_w,_f,this, + _async(_n,_c,_a,_w,apf.lm_exec.c342,this, + LIVEMARKUP END CACHE*/ + var LMENDCACHE; + +})(); + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/core/parsers/url.js)SIZE(4570)TIME(Fri, 14 Oct 2011 08:01:44 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + + +/** + * Object that represents a URI, broken down to its parts, according to RFC3986. + * All parts are publicly accessible after parsing like 'url.port' or 'url.host'. + * Example: + * + * var url = new apf.url('http://usr:pwd@www.test.com:81/dir/dir.2/index.htm?q1=0&&test1&test2=value#top'); + * alert(url.port); //will show '81' + * alert(url.host); //will show 'www.test.com' + * alert(url.isSameLocation()) // will show 'true' when the browser is surfing on the www.test.com domain + * + * + * + * @see http://tools.ietf.org/html/rfc3986 + * @constructor + * @parser + * @default_private + * + * @author Mike de Boer + * @version %I%, %G% + * @since 1.0 + */ +apf.url = function(str) { + var base; + var location = (window.location && window.location.toString()) || ""; + if (str.indexOf(":") == -1 && (base = location).indexOf(":") != -1) { + base = new apf.url(base); + str = apf.getAbsolutePath(base.protocol + "://" + base.host + + (base.directory.charAt(0) == "/" ? "" : "/") + + (base.directory.charAt(base.directory.length - 1) == "/" + ? base.directory + : base.directory + '/'), str).replace(/\/\/\/\//, "///"); + } + var o = apf.url.options, + m = o.parser[o.strictMode ? "strict" : "loose"].exec(str), + i = 14; + this.uri = str.toString(); //copy string + + while (i--) + this[o.key[i]] = m[i] || ""; + + this[o.q.name] = {}; + var _self = this; + this[o.key[12]].replace(o.q.parser, function($0, $1, $2){ + if ($1) + _self[o.q.name][$1] = $2; + }); + + /** + * Checks if the same origin policy is in effect for this URI. + * @see http://developer.mozilla.org/index.php?title=En/Same_origin_policy_for_JavaScript + * + * @returns {Boolean} + */ + this.isSameLocation = function(){ + // filter out anchors + if (this.uri.length && this.uri.charAt(0) == "#") + return false; + // totally relative -- ../../someFile.html + if (!this.protocol && !this.port && !this.host) + return true; + + // scheme relative with port specified -- foo.com:8080 + if (!this.protocol && this.host && this.port + && window.location.hostname == this.host + && window.location.port == this.port) { + return true; + } + // scheme relative with no-port specified -- foo.com + if (!this.protocol && this.host && !this.port + && window.location.hostname == this.host + && window.location.port == 80) { + return true; + } + return window.location.protocol == (this.protocol + ":") + && window.location.hostname == this.host + && (window.location.port == this.port || !window.location.port && !this.port); + } +}; + +apf.url.options = { + strictMode: false, + key: ["source", "protocol", "authority", "userInfo", "user", "password", + "host", "port", "relative", "path", "directory", "file", "query", + "anchor"], + q : { + name : "queryKey", + parser: /(?:^|&)([^&=]*)=?([^&]*)/g + }, + parser: { + strict: /^(?:([^:\/?#]+):)?(?:\/\/((?:(([^:@]*):?([^:@]*))?@)?([^:\/?#]*)(?::(\d*))?))?((((?:[^?#\/]*\/)*)([^?#]*))(?:\?([^#]*))?(?:#(.*))?)/, + loose : /^(?:(?![^:@]+:[^:@\/]*@)([^:\/?#.]+):)?(?:\/\/)?((?:(([^:@]*):?([^:@]*))?@)?([^:\/?#]*)(?::(\d*))?)(((\/(?:[^?#](?![^?#\/]*\.[^?#\/.]+(?:[?#]|$)))*\/?)?([^?#\/]*))(?:\?([^#]*))?(?:#(.*))?)/ + } +}; + + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/core/parsers/xpath.js)SIZE(21971)TIME(Fri, 14 Oct 2011 08:01:44 GMT)*/ + + +/** + * @private + */ +apf.runXpath = function(){ + +/** + * Workaround for the lack of having an XPath parser on safari. + * It works on Safari's document and XMLDocument object. + * + * It doesn't support the full XPath spec, but just enought for + * the skinning engine which needs XPath on the HTML document. + * + * Supports: + * - Compilation of xpath statements + * - Caching of XPath statements + * + * @parser + * @private + */ +apf.XPath = { + cache : {}, + + getSelf : function(htmlNode, tagName, info, count, num, sResult){ + var numfound = 0, result = null, data = info[count]; + + if (data) + data[0](htmlNode, data[1], info, count + 1, numfound++ , sResult); + else + sResult.push(htmlNode); + }, + + getChildNode : function(htmlNode, tagName, info, count, num, sResult){ + var numfound = 0, result = null, data = info[count]; + + var nodes = htmlNode.childNodes; + if (!nodes) return; //Weird bug in Safari + for (var i = 0; i < nodes.length; i++) { + //if (nodes[i].nodeType != 1) + //continue; + + if (tagName && (tagName != nodes[i].tagName) && (nodes[i].style + ? nodes[i].tagName.toLowerCase() + : nodes[i].tagName) != tagName) + continue;// || numsearch && ++numfound != numsearch + + htmlNode = nodes[i]; + + if (data) + data[0](nodes[i], data[1], info, count + 1, numfound++ , sResult); + else + sResult.push(nodes[i]); + } + + //commented out : && (!numsearch || numsearch == numfound) + }, + + doQuery : function(htmlNode, qData, info, count, num, sResult){ + var result = null, data = info[count]; + var query = qData[0]; + var returnResult = qData[1]; + try { + var qResult = eval(query); + }catch(e){ + apf.console.error(e.name + " " + e.type + ":" + apf.XPath.lastExpr + "\n\n" + query); + //throw new Error(e.name + " " + e.type + ":" + apf.XPath.lastExpr + "\n\n" + query); + return; + } + + if (returnResult) + return sResult.push(qResult); + if (!qResult || qResult.dataType == apf.ARRAY && !qResult.length) + return; + + if (data) + data[0](htmlNode, data[1], info, count + 1, 0, sResult); + else + sResult.push(htmlNode); + }, + + getTextNode : function(htmlNode, empty, info, count, num, sResult){ + var data = info[count], + nodes = htmlNode.childNodes; + + for (var i = 0; i < nodes.length; i++) { + if (nodes[i].nodeType != 3 && nodes[i].nodeType != 4) + continue; + + if (data) + data[0](nodes[i], data[1], info, count + 1, i, sResult); + else + sResult.push(nodes[i]); + } + }, + + getAnyNode : function(htmlNode, empty, info, count, num, sResult){ + var data = info[count], + nodes = htmlNode.getElementsByTagName("*");//childNodes; + + for (var i = 0; i < nodes.length; i++) { + if (data) + data[0](nodes[i], data[1], info, count + 1, i, sResult); + else + sResult.push(nodes[i]); + } + }, + + getAttributeNode : function(htmlNode, attrName, info, count, num, sResult){ + if (!htmlNode || htmlNode.nodeType != 1) return; + + if (attrName == "*") { + var nodes = htmlNode.attributes; + for (var i = 0; i < nodes.length; i++) { + arguments.callee.call(this, htmlNode, nodes[i].nodeName, info, + count, i, sResult); + } + return; + } + + var data = info[count], + value = htmlNode.getAttributeNode(attrName);//htmlNode.attributes[attrName];// + + if (data) + data[0](value, data[1], info, count + 1, 0, sResult); + else if (value) + sResult.push(value); + }, + + getAllNodes : function(htmlNode, x, info, count, num, sResult){ + var data = info[count], + tagName = x[0], + inclSelf = x[1], + prefix = x[2], + nodes, i, l; + + if (inclSelf && (htmlNode.tagName == tagName || tagName == "*" || tagName == "node()")) { + if (data) + data[0](htmlNode, data[1], info, count + 1, 0, sResult); + else + sResult.push(htmlNode); + } + + if (tagName == "node()") { + tagName = "*"; + prefix = ""; + if (apf.isIE) { + nodes = htmlNode.getElementsByTagName("*"); + } + else { + nodes = []; + (function recur(x){ + for (var n, i = 0; i < x.childNodes.length; i++) { + n = x.childNodes[i]; + if (n.nodeType != 1) + continue; + nodes.push(n); + + recur(n); + } + })(htmlNode); + } + } + else { + nodes = htmlNode.getElementsByTagName((prefix + && (apf.isGecko || apf.isOpera || htmlNode.nodeFunc) ? prefix + ":" : "") + tagName); + } + + for (i = 0, l = nodes.length; i < l; i++) { + if (data) + data[0](nodes[i], data[1], info, count + 1, i, sResult); + else + sResult.push(nodes[i]); + } + }, + + getAllAncestorNodes : function(htmlNode, x, info, count, num, sResult){ + var data = info[count], + tagName = x[0], + inclSelf = x[1], + i = 0, + s = inclSelf ? htmlNode : htmlNode.parentNode; + while (s && s.nodeType == 1) { + if (s.tagName == tagName || tagName == "*" || tagName == "node()") { + if (data) + data[0](s, data[1], info, count + 1, ++i, sResult); + else + sResult.push(s); + } + s = s.parentNode + } + }, + + getParentNode : function(htmlNode, empty, info, count, num, sResult){ + var data = info[count], + node = htmlNode.parentNode; + + if (data) + data[0](node, data[1], info, count + 1, 0, sResult); + else if (node) + sResult.push(node); + }, + + //precsiblg[3] might not be conform spec + getPrecedingSibling : function(htmlNode, tagName, info, count, num, sResult){ + var data = info[count], + node = htmlNode.previousSibling; + + while (node) { + if (tagName != "node()" && (node.style + ? node.tagName.toLowerCase() + : node.tagName) != tagName){ + node = node.previousSibling; + continue; + } + + if (data) + data[0](node, data[1], info, count+1, 0, sResult); + else if (node) { + sResult.push(node); + break; + } + } + }, + + //flwsiblg[3] might not be conform spec + getFollowingSibling : function(htmlNode, tagName, info, count, num, sResult){ + var result = null, data = info[count]; + + var node = htmlNode.nextSibling; + while (node) { + if (tagName != "node()" && (node.style + ? node.tagName.toLowerCase() + : node.tagName) != tagName) { + node = node.nextSibling; + continue; + } + + if (data) + data[0](node, data[1], info, count+1, 0, sResult); + else if (node) { + sResult.push(node); + break; + } + } + }, + + multiXpaths : function(contextNode, list, info, count, num, sResult){ + for (var i = 0; i < list.length; i++) { + info = list[i][0]; + var rootNode = (info[3] + ? contextNode.ownerDocument.documentElement + : contextNode);//document.body + info[0](rootNode, info[1], list[i], 1, 0, sResult); + } + + sResult.makeUnique(); + }, + + compile : function(sExpr){ + var isAbsolute = sExpr.match(/^\//);//[^\/]/ + + sExpr = sExpr.replace(/\[(\d+)\]/g, "/##$1") + .replace(/\|\|(\d+)\|\|\d+/g, "##$1") + .replace(/\.\|\|\d+/g, ".") + .replace(/\[([^\]]*)\]/g, function(match, m1){ + return "/##" + m1.replace(/\|/g, "_@_"); + }); //wrong assumption think of | + + if (sExpr == "/" || sExpr == ".") + return sExpr; + + //Mark // elements + //sExpr = sExpr.replace(/\/\//g, "/[]/self::"); + + //Check if this is an absolute query + return this.processXpath(sExpr.replace(/\/\//g, "descendant::"), isAbsolute); + }, + + processXpath : function(sExpr, isAbsolute){ + var results = [], + i, l, m, query; + sExpr = sExpr.replace(/'[^']*'/g, function(m){ + return m.replace("|", "_@_"); + }); + + sExpr = sExpr.split("\|"); + for (i = 0, l = sExpr.length; i < l; i++) + sExpr[i] = sExpr[i].replace(/_\@\_/g, "|");//replace(/('[^']*)\_\@\_([^']*')/g, "$1|$2"); + + if (sExpr.length == 1) { + sExpr = sExpr[0]; + } + else { + for (i = 0, l = sExpr.length; i < l; i++) + sExpr[i] = this.processXpath(sExpr[i]); + results.push([this.multiXpaths, sExpr]); + return results; + } + + var sections = sExpr.split("/"); + for (i = 0, l = sections.length; i < l; i++) { + if (sections[i] == "." || sections[i] == "") + continue; + else if (sections[i] == "..") + results.push([this.getParentNode, null]); + else if (sections[i].match(/^[\w\-_\.]+(?:\:[\w\-_\.]+){0,1}$/)) + results.push([this.getChildNode, sections[i]]);//.toUpperCase() + else if (sections[i].match(/^\#\#(\d+)$/)) + results.push([this.doQuery, ["num+1 == " + parseInt(RegExp.$1)]]); + else if (sections[i].match(/^\#\#(.*)$/)) { + //FIX THIS CODE + query = RegExp.$1; + m = [query.match(/\(/g), query.match(/\)/g)]; + if (m[0] || m[1]) { + while (!m[0] && m[1] || m[0] && !m[1] + || m[0].length != m[1].length){ + if (!sections[++i]) break; + query += "/" + sections[i]; + m = [query.match(/\(/g), query.match(/\)/g)]; + } + } + + results.push([this.doQuery, [this.compileQuery(query)]]); + } + else if (sections[i] == "*") + results.push([this.getChildNode, null]); //FIX - put in def function + else if (sections[i].substr(0,2) == "[]") + results.push([this.getAllNodes, ["*", false]]);//sections[i].substr(2) || + else if (sections[i].match(/descendant-or-self::node\(\)$/)) + results.push([this.getAllNodes, ["*", true]]); + else if (sections[i].match(/descendant-or-self::([^\:]*)(?:\:(.*)){0,1}$/)) + results.push([this.getAllNodes, [RegExp.$2 || RegExp.$1, true, RegExp.$1]]); + else if (sections[i].match(/descendant::([^\:]*)(?:\:(.*)){0,1}$/)) + results.push([this.getAllNodes, [RegExp.$2 || RegExp.$1, false, RegExp.$1]]); + else if (sections[i].match(/ancestor-or-self::([^\:]*)(?:\:(.*)){0,1}$/)) + results.push([this.getAllAncestorNodes, [RegExp.$2 || RegExp.$1, true, RegExp.$1]]); + else if (sections[i].match(/ancestor::([^\:]*)(?:\:(.*)){0,1}$/)) + results.push([this.getAllAncestorNodes, [RegExp.$2 || RegExp.$1, false, RegExp.$1]]); + else if (sections[i].match(/^\@(.*)$/)) + results.push([this.getAttributeNode, RegExp.$1]); + else if (sections[i] == "text()") + results.push([this.getTextNode, null]); + else if (sections[i] == "node()") + results.push([this.getChildNode, null]);//FIX - put in def function + else if (sections[i].match(/following-sibling::(.*)$/)) + results.push([this.getFollowingSibling, RegExp.$1.toLowerCase()]); + else if (sections[i].match(/preceding-sibling::(.*)$/)) + results.push([this.getPrecedingSibling, RegExp.$1.toLowerCase()]); + else if (sections[i] == "self::node()") + results.push([this.getSelf, null]); + else if (sections[i].match(/self::(.*)$/)) + results.push([this.doQuery, ["apf.XPath.doXpathFunc(htmlNode, 'local-name') == '" + RegExp.$1 + "'"]]); + else { + //@todo FIX THIS CODE + //add some checking here + query = sections[i]; + m = [query.match(/\(/g), query.match(/\)/g)]; + if (m[0] || m[1]) { + while (!m[0] && m[1] || m[0] && !m[1] || m[0].length != m[1].length) { + if (!sections[++i]) break; + query += "/" + sections[i]; + m = [query.match(/\(/g), query.match(/\)/g)]; + } + } + + results.push([this.doQuery, [this.compileQuery(query), true]]) + + //throw new Error("---- APF Error ----\nMessage : Could not match XPath statement: '" + sections[i] + "' in '" + sExpr + "'"); + } + } + + results[0][3] = isAbsolute; + return results; + }, + + compileQuery : function(code){ + return new apf.CodeCompilation(code).compile(); + }, + + doXpathFunc : function(contextNode, type, nodelist, arg2, arg3, xmlNode, force){ + if (!nodelist || nodelist.length == 0) + nodelist = ""; + + if (type == "not") + return !nodelist; + + if (!force) { + var arg1, i, l; + if (typeof nodelist == "object" || nodelist.dataType == apf.ARRAY) { + if (nodelist && !nodelist.length) + nodelist = [nodelist]; + + var res = false, value; + for (i = 0, l = nodelist.length; i < l; i++) { + xmlNode = nodelist[i]; + if (!xmlNode || typeof xmlNode == "string" + || "position|last|count|local-name|name".indexOf(type) > -1) { + value = xmlNode; + } + else { + if (xmlNode.nodeType == 1 && xmlNode.firstChild && xmlNode.firstChild.nodeType != 1) + xmlNode = xmlNode.firstChild; + value = xmlNode.nodeValue; + } + + if (res = arguments.callee.call(this, contextNode, type, value, arg2, arg3, xmlNode, true)) + return res; + } + return res; + } + else { + arg1 = nodelist; + } + } + else { + arg1 = nodelist; + } + + switch(type){ + case "position": + return apf.getChildNumber(contextNode) + 1; + case "format-number": + return apf.formatNumber(arg1); //@todo this should actually do something + case "floor": + return Math.floor(arg1); + case "ceiling": + return Math.ceil(arg1); + case "starts-with": + return arg1 ? arg1.substr(0, arg2.length) == arg2 : false; + case "string-length": + return arg1 ? arg1.length : 0; + case "count": + return arg1 ? arg1.length : 0; + case "last": + return arg1 ? arg1[arg1.length-1] : null; + case "name": + var c = xmlNode || contextNode; + return c.nodeName || c.tagName; + case "local-name": + var c = xmlNode || contextNode; + if (c.nodeType != 1) return false; + return c.localName || (c.tagName || "").split(":").pop();//[apf.TAGNAME] + case "substring": + return arg1 && arg2 ? arg1.substring(arg2, arg3 || 0) : ""; + case "contains": + return arg1 && arg2 ? arg1.indexOf(arg2) > -1 : false; + case "concat": + var str = "" + for (i = 1, l = arguments.length; i < l; i++) { + if (typeof arguments[i] == "object") { + str += getNodeValue(arguments[i][0]); + continue; + } + str += arguments[i]; + } + return str; + case "translate": + for (i = 0, l = arg2.length; i < l; i++) + arg1 = arg1.replace(arg2.substr(i,1), arg3.substr(i,1)); + return arg1; + } + }, + + selectNodeExtended : function(sExpr, contextNode, match){ + var sResult = this.selectNodes(sExpr, contextNode); + + if (sResult.length == 0) + return null; + if (!match) + return sResult; + + for (var i = 0, l = sResult.length; i < l; i++) { + if (String(getNodeValue(sResult[i])) == match) + return [sResult[i]]; + } + + return null; + }, + + getRoot : function(xmlNode){ + while (xmlNode.parentNode && xmlNode.parentNode.nodeType == 1) + xmlNode = xmlNode.parentNode; + + return xmlNode.parentNode; + }, + + selectNodes : function(sExpr, contextNode){ + if (!this.cache[sExpr]) + this.cache[sExpr] = this.compile(sExpr); + + + + if (typeof this.cache[sExpr] == "string"){ + if (this.cache[sExpr] == ".") + return [contextNode]; + if (this.cache[sExpr] == "/") { + return [(contextNode.nodeType == 9 + ? contextNode.documentElement + : this.getRoot(contextNode))]; + } + } + + if (typeof this.cache[sExpr] == "string" && this.cache[sExpr] == ".") + return [contextNode]; + + var info = this.cache[sExpr][0], + rootNode = (info[3] + ? (contextNode.nodeType == 9 + ? contextNode.documentElement + : this.getRoot(contextNode)) + : contextNode),//document.body*/ + sResult = []; + + if (rootNode) + info[0](rootNode, info[1], this.cache[sExpr], 1, 0, sResult); + + return sResult; + } +}; + +function getNodeValue(sResult){ + if (sResult.nodeType == 1) + return sResult.firstChild ? sResult.firstChild.nodeValue : ""; + if (sResult.nodeType > 1 || sResult.nodeType < 5) + return sResult.nodeValue; + return sResult; +} + +/** + * @constructor + * @private + */ +apf.CodeCompilation = function(code){ + this.data = { + F : [], + S : [], + I : [], + X : [] + }; + + this.compile = function(){ + code = code.replace(/ or /g, " || ") + .replace(/ and /g, " && ") + .replace(/!=/g, "{}") + .replace(/=/g, "==") + .replace(/\{\}/g, "!="); + + // Tokenize + this.tokenize(); + + // Insert + this.insert(); + + code = code.replace(/, \)/g, ", htmlNode)"); + + return code; + }; + + this.tokenize = function(){ + //Functions + var data = this.data.F; + code = code.replace(/(translate|format-number|contains|substring|local-name|last|position|round|starts-with|string|string-length|sum|floor|ceiling|concat|count|not)\s*\(/g, + function(d, match){ + return (data.push(match) - 1) + "F_"; + } + ); + + //Strings + data = this.data.S; + code = code.replace(/'([^']*)'/g, function(d, match){ + return (data.push(match) - 1) + "S_"; + }) + .replace(/"([^"]*)"/g, function(d, match){ + return (data.push(match) - 1) + "S_"; + }); + + //Xpath + data = this.data.X; + code = code.replace(/(^|\W|\_)([\@\.\/A-Za-z\*][\*\.\@\/\w\:\-]*(?:\(\)){0,1})/g, + function(d, m1, m2){ + return m1 + (data.push(m2) - 1) + "X_"; + }) + .replace(/(\.[\.\@\/\w]*)/g, function(d, m1, m2){ + return (data.push(m1) - 1) + "X_"; + }); + + //Ints + data = this.data.I; + code = code.replace(/(\d+)(\W)/g, function(d, m1, m2){ + return (data.push(m1) - 1) + "I_" + m2; + }); + }; + + this.insert = function(){ + var data = this.data; + code = code.replace(/(\d+)X_\s*==\s*(\d+S_)/g, function(d, nr, str){ + return "apf.XPath.selectNodeExtended('" + + data.X[nr].replace(/'/g, "\\'") + "', htmlNode, " + str + ")"; + }) + .replace(/(\d+)([FISX])_/g, function(d, nr, type){ + var value = data[type][nr]; + + if (type == "F") { + return "apf.XPath.doXpathFunc(htmlNode, '" + value + "', "; + } + else if (type == "S") { + return "'" + value + "'"; + } + else if (type == "I") { + return value; + } + else if (type == "X") { + return "apf.XPath.selectNodeExtended('" + + value.replace(/'/g, "\\'") + "', htmlNode)"; + } + }) + .replace(/, \)/g, ")"); + }; +}; + +} + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/elements/bindingrule.js)SIZE(8842)TIME(Tue, 13 Dec 2011 17:11:58 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + + +/** + * @define bindings element containing all the binding rules for the data + * bound elements referencing this element. + * Example: + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * @see element.smartbinding + * + * @baseclass + * + * @author Ruben Daniels (ruben AT ajax DOT org) + * @version %I%, %G% + * @since 0.8 + * + * @default_private + */ +apf.BindingRule = function(struct, tagName){ + this.$init(tagName || true, apf.NODE_HIDDEN, struct); +}; + +(function(){ + this.$bindingRule = true; + + this.compile = function(prop){ + return (this["c" + prop] = apf.lm.compile(this[prop], { + xpathmode : 3, + injectself : true + })); + }; + + this.$compile = function(prop, options){ + return (this["c" + prop + "2"] = apf.lm.compile(this[prop], options)); + }; + + //1 = force no bind rule, 2 = force bind rule + this.$attrExcludePropBind = apf.extend({ + value : 1, + match : 1 + }, this.$attrExcludePropBind); + + this.$booleanProperties["hasaml"] = true; + + this.$propHandlers["value"] = + this.$propHandlers["match"] = function(value, prop){ + delete this["c" + prop]; + + if (this.$amlLoaded) { + //Find parent that this rule works on + var node = this; + while (node && node.$bindingRule) + node = node.parentNode; + + if (!node) return; + + //Reload parent to propagate change + apf.queue.add("reload" + node.$uniqueId, function(){ + node.reload(); + }); + + //Recompile ruleset + if (node.$bindings.$isCompiled) + node.$bindings.$compiled = node.$bindings.compile( + this.localName != "each" && this.localName); + } + }; + + /**** DOM Handlers ****/ + + /*this.addEventListener("DOMAttrModified", function(e){ + + });*/ + + this.addEventListener("DOMNodeInserted", function(e){ + //Find parent that this rule works on + var node = this; + while (node.$bindingRule) + node = node.parentNode; + + //Reload parent to propagate change + //@todo trigger should be maintained on node itself to prevent dual reload + if ("expanded|collapsed".indexOf(this.localName) == -1) + apf.queue.add("reload" + node.$uniqueId, function(){ + node.reload(); + }); + + //If this node is added, add to set + if (e.currentTarget == this) { + (node.$bindings[this.localName] + || (node.$bindings[this.localName] = [])).pushUnique(this); + } + //@todo apf3.0 test if proc instr and cdata needs to be serialized + //Else just update the binding value + else if (!this.attributes.getNamedItem("value")) + this.value = apf.serializeChildren(this); + //Or do nothing + else return; + + //Recompile ruleset + if (node.$bindings.$isCompiled) + node.$bindings.$compiled = node.$bindings.compile( + this.localName != "each" && this.localName); + }); + + this.addEventListener("DOMNodeRemoved", function(e){ + if (this.$amlDestroyed) + return; + + //Find parent that this rule works on + var first, node = this; + while (node && node.$bindingRule) + node = node.parentNode; + + if (!node) + return; + + //If this node is removed, remove to set + if (e.currentTarget == this) { + if (node.$bindings && node.$bindings[this.localName]) + node.$bindings[this.localName].remove(this); + else + return; + } + //@todo apf3.0 test if proc instr and cdata needs to be serialized + //Else just update the binding value + else if (!this.attributes.getNamedItem("value") && (first = this.firstChild)) { + if (first.nodeType == this.NODE_PROCESSING_INSTRUCTION) { + if (first.target == "lm") + this.value = "{" + first.nodeValue + "}"; + else + this.value = first.nodeValue; + } + else + this.value = apf.serializeChildren(this).trim(); + } + //Or do nothing + else return; + + //Reload parent to propagate change + if ("expanded|collapsed".indexOf(this.localName) == -1) + apf.queue.add("reload" + node.$uniqueId, function(){ + if(!node.$amlDestroyed) + node.reload(); + }); + + //Recompile ruleset + if (node.$bindings.$isCompiled) + node.$bindings.$compiled = node.$bindings.compile( + this.localName != "each" && this.localName); + }); + + this.addEventListener("DOMNodeInsertedIntoDocument", function(e){ + + + var first; + if (!this.value && this.localName != "each" && (first = this.$aml + && this.$aml.firstChild || this.firstChild)) { + if (first.nodeType == this.NODE_PROCESSING_INSTRUCTION) { + if (first.target == "lm") + this.value = "{" + first.nodeValue + "}"; + else + this.value = first.nodeValue; + } + else + this.value = apf.serializeChildren(this.$aml).trim(); + } + + //Find the parent this rule works on + var pNode = this.parentNode; + while (pNode.$bindingRule) + pNode = pNode.parentNode; + + //Add the rule to the set + var bindings = pNode.$bindings || (pNode.$bindings = new apf.ruleList()); + (bindings[this.localName] || (bindings[this.localName] = [])).push(this); + + //Compile if necessary + if (pNode.localName != "bindings" && (this.localName != "each" || !this.childNodes.length)) { + var ns = this; + while((ns = ns.nextSibling) && ns.nodeType != 1); + + if (!ns || !ns.$bindingRule) { + pNode.$cbindings = pNode.$bindings.compile( + pNode.$bindings.$isCompiled ? this.localName : null); + + pNode.dispatchEvent("bindingsload", { + bindings: pNode.$bindings, + compiled: pNode.$cbindings + }); + pNode.$checkLoadQueue(); + } + } + }); +}).call(apf.BindingRule.prototype = new apf.AmlElement()); + +apf.aml.setElement("icon", apf.BindingRule); +apf.aml.setElement("image", apf.BindingRule); +apf.aml.setElement("caption", apf.BindingRule); +apf.aml.setElement("tooltip", apf.BindingRule); +apf.aml.setElement("css", apf.BindingRule); +apf.aml.setElement("selectable", apf.BindingRule); +apf.aml.setElement("value", apf.BindingRule); +apf.aml.setElement("src", apf.BindingRule); +apf.aml.setElement("collapsed", apf.BindingRule); +apf.aml.setElement("expanded", apf.BindingRule); +apf.aml.setElement("empty", apf.BindingRule); + + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/elements/accordion.js)SIZE(22288)TIME(Fri, 14 Oct 2011 08:01:44 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/elements/actionrule.js)SIZE(4035)TIME(Fri, 14 Oct 2011 08:01:44 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + + +/** + * @attribute {String} match + * @attribute {String} set + * @attribute {String} undo + * @attribute {String} lock + * @define update + * @attribute {String} get + * @attribute {String} parent + * @define add + * @attribute {Boolean} get + * @attribute {Boolean} parent + */ +apf.ActionRule = function(struct, tagName){ + this.$init(tagName || true, apf.NODE_HIDDEN, struct); +}; + +(function(){ + this.$actionRule = true; + + this.compile = function(prop, options){ + return (this["c" + prop] = apf.lm.compile(this[prop], + options || {xpathmode: 2})); + } + + //1 = force no bind rule, 2 = force bind rule + this.$attrExcludePropBind = apf.extend({ + set : 1, + get : 1, + undo : 1, + lock : 1, + match : 1, + parent : 1 + }, this.$attrExcludePropBind); + + this.$propHandlers["set"] = + this.$propHandlers["get"] = + this.$propHandlers["parent"] = + this.$propHandlers["match"] = function(value, prop){ + delete this["c" + prop]; + } + + /**** DOM Handlers ****/ + + this.addEventListener("DOMNodeInserted", function(e){ + if (e.currentTarget == this) { + var pNode = this.parentNode; + if (!pNode.$actions) + pNode.$actions = new apf.ruleList(); + + (pNode.$actions[this.localName] + || (pNode.$actions[this.localName] = [])).push(this); + } + else { + if (this.attributes.getNamedItem("value")) + return; + + //@todo apf3.0 test if proc instr and cdata needs to be serialized + this.value = apf.serializeChildren(this); + } + }); + + this.addEventListener("DOMNodeRemoved", function(e){ + if (this.$amlDestroyed) + return; + + if (e.currentTarget == this) { + this.parentNode.$actions[this.localName].remove(this); + } + else { + if (this.attributes.getNamedItem("value")) + return; + + //@todo apf3.0 test if proc instr and cdata needs to be serialized + this.value = apf.serializeChildren(this); + } + }); + + this.addEventListener("DOMNodeInsertedIntoDocument", function(e){ + if (!this.get) + this.get = apf.serializeChildren(this.$aml).trim(); + + var actions = this.parentNode.$actions + || (this.parentNode.$actions = new apf.ruleList()); + + (actions[this.localName] || (actions[this.localName] = [])).push(this); + }); +}).call(apf.ActionRule.prototype = new apf.AmlElement()); + +apf.aml.setElement("rename", apf.ActionRule); +apf.aml.setElement("remove", apf.ActionRule); +apf.aml.setElement("add", apf.ActionRule); +apf.aml.setElement("update", apf.ActionRule); +apf.aml.setElement("copy", apf.ActionRule); +apf.aml.setElement("move", apf.ActionRule); +apf.aml.setElement("check", apf.ActionRule); +apf.aml.setElement("change", apf.ActionRule); + + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/elements/actions.js)SIZE(3251)TIME(Fri, 14 Oct 2011 08:01:44 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + + +/** +* @define actions element containing all the action rules for the data + * bound elements referencing this element. + * Example: + * + * + * + * + * + * + * + * + * + * + * @allowchild {actions} + * @addnode smartbinding, global + * + * @constructor + * @apfclass + * + * @author Ruben Daniels (ruben AT ajax DOT org) + * @version %I%, %G% + * @since 0.8 + * + * @default_private + */ +apf.actions = function(struct, tagName){ + this.$init(tagName || "actions", apf.NODE_HIDDEN, struct); + + this.$actions = new apf.ruleList(); + this.$amlNodes = {}; +}; + +(function(){ + this.$smartbinding = null; + + this.register = function(amlNode){ + if (amlNode.localName == "smartbinding") { + this.$smartbinding = amlNode; + this.$smartbinding.add(this); //Assuming only at init + return; + } + + this.$amlNodes[amlNode.$uniqueId] = amlNode; + amlNode.$actions = this.$actions; + amlNode.$actionsElement = this; + amlNode.dispatchEvent("actionsload", {bindings: this}); + } + + this.unregister = function(amlNode){ + //unregister element + this.$amlNodes[amlNode.$uniqueId] = null; + delete this.$amlNodes[amlNode.$uniqueId]; + + delete amlNode.$actionsElement; + delete amlNode.$actions; + amlNode.dispatchEvent("actionsunload", {bindings: this}); + }; + + /**** DOM Handlers ****/ + + this.addEventListener("DOMNodeInsertedIntoDocument", function(e){ + var nodes = this.childNodes; + for (var node, i = 0, l = nodes.length; i < l; i++) { + if ((node = nodes[i]).nodeType != 1) + continue; + + node.dispatchEvent("DOMNodeInsertedIntoDocument"); + } + + this.register(this.parentNode); + }); +}).call(apf.actions.prototype = new apf.AmlElement()); + +apf.aml.setElement("actions", apf.actions); + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/elements/actiontracker.js)SIZE(36637)TIME(Wed, 30 Nov 2011 17:29:20 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + + +/** + * Element keeping track of all user actions that are triggered in GUI + * elements. This element maintains a stack of actions and knows how to + * undo & redo them. It is aware of how to synchronize the changes to the + * backend data store. + * Example: + * + * datagrid.getActionTracker().undo(); + * + * Remarks: + * With offline support enabled the actiontracker can + * serialize both its undo stack and its execution stack such that these can + * be kept in between application sessions. This means that a user will be able + * to close the application and start it at a later date whilst keeping his or + * her entire undo/redo stack. Furthermore all changes done whilst being offline + * will be synchronized to the data store when the application comes online. + * + * @constructor + * @inherits apf.Class + * + * @define actiontracker + * @addnode smartbinding, global + * @event afterchange Fires after a change to the action stack occurs + * object: + * {String} action the name of the action that was execution + * @event beforechange Fires before a change to the action stack will occur + * cancelable: Prevents the execution of the action. + * object: + * {String} action the action to be executed + * {Array} args the arguments for the action + * {XmlNode} [xmlActionNode] the rules to synchronize the changes to the server + * for both execution and undo. (See action rules) + * {AmlNode} [amlNode] the GUI element that triggered the action + * {XmlNode} [selNode] the relevant {@link term.datanode data node} to + * which the action node works on + * {Number} [timestamp] the start of the action that is now executed. + * @event actionfail Fires when an action fails to be sent to the server. + * bubbles: true + * object: + * {Error} error the error object that is thrown when the event + * callback doesn't return false. + * {Number} state the state of the call + * Possible values: + * apf.SUCCESS the request was successfull + * apf.TIMEOUT the request has timed out. + * apf.ERROR an error has occurred while making the request. + * apf.OFFLINE the request was made while the application was offline. + * {mixed} userdata data that the caller wanted to be available in + * the callback of the http request. + * {XMLHttpRequest} http the object that executed the actual http request. + * {String} url the url that was requested. + * {Http} tpModule the teleport module that is making the request. + * {Number} id the id of the request. + * {String} message the error message. + * @see term.locking + * @event actionsuccess Fires when an action is successfully sent to the server. + * bubbles: true + * object: + * {Number} state the state of the call + * Possible values: + * apf.SUCCESS the request was successfull + * apf.TIMEOUT the request has timed out. + * apf.ERROR an error has occurred while making the request. + * apf.OFFLINE the request was made while the application was offline. + * {mixed} userdata data that the caller wanted to be available in + * the callback of the http request. + * {XMLHttpRequest} http the object that executed the actual http request. + * {String} url the url that was requested. + * {Http} tpModule the teleport module that is making the request. + * {Number} id the id of the request. + * + * @author Ruben Daniels (ruben AT ajax DOT org) + * @version %I%, %G% + * @since 0.8 + */ +apf.actiontracker = function(struct, tagName){ + this.$init(tagName || "actiontracker", apf.NODE_HIDDEN, struct); + + this.$undostack = []; + this.$redostack = []; + this.$execstack = []; + +}; + +(function(){ + this.$lastExecStackItem = null; + this.$paused = false; + + this.realtime = true; + this.undolength = 0; + this.redolength = 0; + + + /** + * @attribute {Number} !undolength the length of the undo stack. + * @attribute {Number} !redolength the length of the redo stack. + * @attribute {Number} !length the length of the undo/redo stack combined. + * Use this attribute to bind a slider's max + * attribute to. + * @attribute {Number} position the position within the total length (same + * value as undolength). Use this attribute + * to bind a slider's value attribute to. + * @attribute {Boolean} realtime whether changes are immediately send to + * the datastore, or held back until purge() is called. + */ + this.$booleanProperties = {}; + this.$booleanProperties["realtime"] = true; + this.$supportedProperties = ["realtime", "undolength", "redolength", "alias", "length", "position"]; + this.$handlePropSet = function(prop, value, force){ + if (this.$booleanProperties[prop]) + value = apf.isTrue(value); + + //Read only properties + switch (prop) { + case "undolength": + this.undolength = this.$undostack.length; + + break; + case "redolength": + this.redolength = this.$redostack.length; + break; + + + default: + this[prop] = value; + } + }; + + /*this.addEventListener("DOMNodeInsertedIntoDocument", function(e){ + if (this.parentNode) + this.parentNode.$at = this; + });*/ + + + this.item = function(index){ + if (index < this.$undostack.length) + return this.$undostack[index]; + + return this.$redostack[index - this.$undostack.length]; + } + + this.add = function(data, title){ + throw new Error("Not implemented yet"); + } + + this.remove = function(index){ + throw new Error("Not implemented yet"); + } + + this.clearUndo = function(){ + this.setProperty("undolength", this.$undostack.length = 0); + + + this.dispatchEvent("afterchange", {action: "clear-undo"}); + } + + this.clearRedo = function(){ + this.setProperty("redolength", this.$redostack.length = 0); + + + this.dispatchEvent("afterchange", {action: "clear-redo"}); + } + + + /** + * Adds a new action handler which can be used by any actiontracker. + * @param {String} action Specifies the name of the action + * @param {Function} func Specifies the function that is executed when + * Executing or undoing the action. + */ + this.define = function(action, func){ + apf.actiontracker.actions[action] = func; + }; + + /** + * Searches for the actiontracker that functions as a parent for this one. + * @return {ActionTracker} Returns the parent actiontracker + */ + this.getParent = function(){ + return this.parentNode && this.parentNode.getActionTracker + ? this.parentNode.getActionTracker(true) + : (apf.window.$at != this ? apf.window.$at : null); + }; + + this.getDone = function(time) { + if (typeof time != "number") + return []; + for (var o, i = this.$undostack.length; i >= 0; --i) { + if (!(o = this.$undostack[i]) || !o.timestamp) continue; + if (o.timestamp >= time) + return this.$undostack.slice(i); + } + return []; + }; + + this.getUndone = function(time) { + if (typeof time != "number") + return []; + for (var o, i = 0, l = this.$redostack.length; i < l; ++i) { + if (!(o = this.$redostack[i]) || !o.timestamp) continue; + if (o.timestamp <= time) + return this.$redostack.slice(0, i + 1); + } + return []; + }; + + /** + * Executes an action, which later can be undone and of which the execution + * can be synchronized to the data store. + * @param {Object} options the details of the execution. + * Properties: + * {String} action the action to be executed + * {Array} args the arguments for the action + * {XmlNode} [xmlActionNode] the rules to synchronize the changes to the + * server for both execution and undo. (See action rules) + * {AmlNode} [amlNode] the GUI element that triggered the action + * {XmlNode} [selNode] the relevant {@link term.datanode data node} + * to which the action node works on + * {Number} [timestamp] the start of the action that is now executed. + * {String} [annotator] the name or identifier of the entity that is + * responsible for the action + */ + this.execute = function(options){ + if (this.dispatchEvent("beforechange", options) === false) + return false; + + //Execute action + var UndoObj = new apf.UndoData(options, this); + if (options.action && !options.transaction) + apf.actiontracker.actions[options.action](UndoObj, false, this); + + if (!this.$paused) { + //Add action to stack + UndoObj.id = this.$undostack.push(UndoObj) - 1; + } + + this.setProperty("undolength", this.$undostack.length); + + + + //Respond + if (UndoObj.multiple) + this.$addToQueue(UndoObj.multiple, false, true); + else + this.$addToQueue(UndoObj, false); + + //Reset Redo Stack + // @todo for rdb refactor we have to deal with collision handling within the at + if (!options.rdb) { + this.$redostack.length = 0; + this.setProperty("redolength", this.$redostack.length); + + + } + + this.dispatchEvent("afterchange", { + action : "do" + }) + + //return stack id of action + return UndoObj; + }; + + this.pauseTracking = function() { + this.$paused = true; + }; + + this.resumeTracking = function() { + this.$paused = false; + }; + + this.isTracking = function() { + return !this.$paused; + }; + + //deprecated?? + /*this.$addActionGroup = function(done, rpc){ + var UndoObj = new apf.UndoData("group", null, [ + //@todo apf.copyArray is deprecated and no longer exists + apf.copyArray(done, UndoData), apf.copyArray(rpc, UndoData) + ]); + this.$undostack.push(UndoObj); + this.setProperty("undolength", this.$undostack.length); + + //@todo reset redo here? + + + + this.dispatchEvent("afterchange", {action: "group", done: done}); + };*/ + + /** + * Synchronizes all held back changes to the data store. + * @todo I don't really know if this stacking into the parent is + * still used, for instance for apf.Transaction. please think + * about it. + */ + this.purge = function(nogrouping, forcegrouping){//@todo, maybe add noReset argument + //var parent = this.getParent(); + + //@todo Check if this still works together with transactions + if (true) {//nogrouping && parent + if (this.$execstack.length) { + this.$execstack[0].undoObj.saveChange(this.$execstack[0].undo, this); + this.$lastExecStackItem = this.$execstack[this.$execstack.length - 1]; + } + } + else if (parent) { + /* + Copy Stacked Actions as a single + grouped action to parent ActionTracker + */ + //parent.$addActionGroup(this.$undostack, stackRPC); + + //Reset Stacks + this.reset(); + } + }; + + + + /** + * Empties the action stack. After this method is run running undo + * or redo will not do anything. + */ + this.reset = function(){ + this.$undostack.length = this.$redostack.length = 0; + this.$paused = false; + + this.setProperty("undolength", 0); + this.setProperty("redolength", 0); + + + this.dispatchEvent("afterchange", {action: "reset"}); + }; + + /** + * Revert the most recent action on the action stack + */ + this.undo = function(id, single, rollback){ + change.call(this, id, single, true, rollback); + }; + + /** + * Re-executes the last undone action + */ + this.redo = function(id, single, rollback){ + change.call(this, id, single, false, rollback); + }; + + function change(id, single, undo, rollback){ + var undoStack = undo ? this.$undostack : this.$redostack, //local vars switch + redoStack = undo ? this.$redostack : this.$undostack; //local vars switch + + if (!undoStack.length) return; + + if (single) { + var UndoObj = undoStack[id]; + if (!UndoObj) return; + + + undoStack.length--; + redoStack.push(UndoObj); //@todo check: moved from outside if(single) + + + + //Undo Client Side Action + if (UndoObj.action) + apf.actiontracker.actions[UndoObj.action](UndoObj, undo, this); + + if (!rollback) { + if (UndoObj.multiple) + this.$addToQueue(UndoObj.multiple, undo, true); + else + this.$addToQueue(UndoObj, undo); + } + + //Set Changed Value + this.setProperty("undolength", this.$undostack.length); + this.setProperty("redolength", this.$redostack.length); + return UndoObj; + } + + if (this.dispatchEvent("beforechange") === false) + return; + + + + //Undo the last X places - where X = id; + if (id == -1) + id = undoStack.length; + + if (!id) + id = 1; + + var i = 0; + while (i < id && undoStack.length > 0) { + if (!undoStack[undoStack.length - 1]) { + undoStack.length--; + + + + this.$undostack = []; + this.$redostack = []; + + + + return false; + } + else { + change.call(this, undoStack.length - 1, true, undo, rollback); + i++; + } + } + + this.dispatchEvent("afterchange", { + action : undo ? "undo" : "redo", + rollback : rollback + }) + } + + this.$receive = function(data, state, extra, UndoObj, callback){ + if (state == apf.TIMEOUT + && extra.tpModule.retryTimeout(extra, state, this) === true) + return true; + + if (state != apf.SUCCESS) { + //Tell anyone that wants to hear about our failure :( + if (this.dispatchEvent("actionfail", apf.extend(extra, { + state : state, + message : "Could not sent Action RPC request for control " + + this.name + + "[" + this.localName + "] \n\n" + + extra.message, + bubbles : true + })) === false) { + + + + return true; //don't delete the call from the queue + } + + /* + Undo the failed action. We're only undoing one item of the stack + if the developer has told us using the @ignore-fail attribute + that it's ok, the data will be safe if we undo only this one. + + @todo: Shouldn't the stackUndone be cleared after this... or + is it intuitive enough for the user that redo will + let the user retry the action?? + */ + if (typeof apf.offline != "undefined" && !apf.offline.reloading) + this.undo(UndoObj.id, extra.userdata, true); + + if (callback) + callback(!extra.userdata); + + if (!extra.userdata) { + /* + Clearing the execStack, none of the changes will be send to + the server. This seems the best way right now and is related + to the todo item above. + + @todo: Think about adding ignore-fail to settings and + actiontracker. + */ + this.$execstack = []; + + var oError = new Error(apf.formatErrorString(0, this, + "Executing action", + "Error sending action to the server:\n" + + (extra.url ? "Url:" + extra.url + "\n\n" : "") + + extra.message)); + + //(UndoObj && UndoObj.xmlActionNode || extra.amlNode || apf) + if (this.dispatchEvent("error", apf.extend({ + error : oError, + state : state, + bubbles : true + }, extra)) === false) + return; + + throw oError; + } + } + else { + //Tell anyone that wants to hear about our success + this.dispatchEvent("actionsuccess", apf.extend(extra, { + state : state, + bubbles : true + }, extra)); + + + + if (callback) + callback(); + } + + this.$queueNext(UndoObj, callback); + }; + + this.$addToQueue = function(UndoObj, undo, isGroup){ + /* + Remove item from the execution stack if it's not yet executed + to keep the stack clean + */ + //@todo Implement this for isGroup if deemed useful + if (!isGroup && this.$execstack.length && !UndoObj.state + && this.$execstack[this.$execstack.length - 1].undoObj == UndoObj) { + this.$execstack.length--; + + + + + + return; + } + + var idx, undoObj, qItem; + // Add the item to the queue + if (isGroup) { //@todo currently no offline support for grouped actions + var undoObj, qItem = this.$execstack.shift(); + for (var i = 0; i < UndoObj.length; i++) { + undoObj = UndoObj[i]; + this.$execstack.unshift({ + undoObj : (undoObj.tagName + ? undoObj + : new apf.UndoData(undoObj, this)).preparse(undo, this), + undo : undo + }); + } + if (qItem) + this.$execstack.unshift(qItem); + + return; + } + + qItem = { + undoObj: UndoObj.preparse(undo, this), + undo : undo + + }; + this.$execstack.push(qItem) - 1; + + + + //The queue was empty, yay! we're gonna exec immediately + if (this.$execstack.length == 1 && this.realtime) + UndoObj.saveChange(undo, this); + }; + + this.$queueNext = function(UndoObj, callback){ + /* + These thow checks are so important, that they are also executed + in release mode. + */ + if (!this.$execstack[0] || this.$execstack[0].undoObj != UndoObj){ + throw new Error(apf.formatErrorString(0, this, "Executing Next \ + action in queue", "The execution stack was corrupted. This is \ + a fatal error. The application should be restarted. You will \ + lose all your changes. Please contact the administrator.")); + } + + //Reset the state of the undo item + UndoObj.state = null; + + //Remove the action item from the stack + var lastItem = this.$execstack.shift(); + + + + //Check if there is a new action to execute; + if (!this.$execstack[0] || lastItem == this.$lastExecStackItem) + return; + + // @todo you could optimize this process by using multicall, but too much for now + + //Execute action next in queue + this.$execstack[0].undoObj.saveChange(this.$execstack[0].undo, this, callback); + }; + + +}).call(apf.actiontracker.prototype = new apf.AmlElement()); + +apf.aml.setElement("actiontracker", apf.actiontracker); + + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/elements/application.js)SIZE(1834)TIME(Fri, 14 Oct 2011 08:01:44 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + + +/** + * @todo description + * + * @author Ruben Daniels (ruben AT ajax DOT org) + * @version %I%, %G% + * @since 0.4 + */ +apf.application = function(){ + this.$init("application", apf.NODE_HIDDEN); + + if (!apf.isO3) { + this.$int = document.body; + this.$tabList = []; //Prevents documentElement from being focussed + this.$focussable = apf.KEYBOARD; + this.focussable = true; + this.visible = true; + this.$isWindowContainer = true; + this.focus = function(){ this.dispatchEvent("focus"); }; + this.blur = function(){ this.dispatchEvent("blur"); }; + + + apf.window.$addFocus(this); + + } +}; +apf.application.prototype = new apf.AmlElement(); +apf.aml.setElement("application", apf.application); + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/elements/appsettings.js)SIZE(9304)TIME(Fri, 14 Oct 2011 08:01:44 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + + +/** + * Element specifying the settings of the application. + * @define appsettings + * @addnode global + * @attribute {Boolean} debug whether the debug screen is shown at startup. + * @see core.apf.object.debugwin + * @see core.apf.object.console + * @attribute {String} name the name of the application, used by many different services to uniquely identify the application. + * @attribute {Boolean} disable-right-click whether a user can get the browsers contextmenu when the right mouse button is clicked. + * @see element.contextmenu + * @attribute {Boolean} allow-select whether general text in the application can be selected. + * @attribute {Boolean} allow-blur whether it's possible to blur an element while not giving the focus to another element. Defaults to true. + * @attribute {Boolean} auto-disable-actions whether smartbinding actions are by default disabled. + * @see term.action + * @attribute {Boolean} auto-disable whether elements that don't have content loaded are automatically disabled. + * @attribute {Boolean} disable-f5 whether the F5 key for refreshing is disabled. + * @attribute {Boolean} auto-hide-loading whether the load screen defined by the loader element is automatically hidden. Setting this to false enables you to control when the loading screen is hidden. Use the following code to do so: + * + * apf.document.getElementsByTagName("a:loader")[0].hide() + * //or + * loaderId.hide() + * + * @attribute {Boolean} disable-space whether the space button default behaviour of scrolling the page is disabled. + * @attribute {Boolean} disable-backspace whether the backspace button default behaviour of going to the previous history state is disabled. + * @attribute {String} default-page the name of the default page if none is specified using the #. Defaults to "home". See {object.history}. + * @see element.history + * @attribute {Boolean} undokeys whether the undo and redo keys (in windows they are ctrl-Z and ctrl-Y) are enabled. + * @see element.actiontracker + * @attribute {String, Boolean} outline whether an outline of an element is shown while dragging or resizing. + * @see baseclass.interactive + * @attribute {String, Boolean} drag-outline whether an outline of an element is shown while dragging. + * @see baseclass.interactive + * @attribute {String, Boolean} resize-outline whether an outline of an element is shown while resizing. + * @see baseclass.interactive + * @attribute {String} baseurl the basepath for any relative url used throughout your application. This included teleport definitions and {@link term.datainstruction data instruction}. + * @see teleport.http + * @see term.datainstruction + * @attribute {String} loading-message the global value for the loading message of elements during a loading state. + * @see baseclass.databinding.attribute.loading-message + * @attribute {String} offline-message the global value for the offline message of elements not able to display content while offline. + * @see baseclass.databinding.attribute.offline-message + * @attribute {String} empty-message the global value for the empty message of elements containing no contents. + * @see baseclass.databinding.attribute.empty-message + * @attribute {String} model the default model for this application. + * @see element.model + * @attribute {String} realtime the global value whether bound values are realtime updated. When set to false elements do not update until they lose focus. + * @see element.editor.attribute.realtime + * @see element.textbox.attribute.realtime + * @see element.slider.attribute.realtime + * @attribute {String} skinset the skin set used by the application. + * @see baseclass.presentation.attribute.skinset + * @attribute {String} storage the {@link core.storage storage provider} to be used for key/value storage. + * @see core.storage + * @attribute {String} offline the {@link core.storage storage provider} to be used for offline support. + * @see element.offline + * @attribute {String} login the {@link term.datainstruction data instruction} which logs a user into the application. + * @see element.auth + * @attribute {String} logout the {@link term.datainstruction data instruction} which logs a user out of the application. + * @see element.auth + * @attribute {String} iepngfix whether the fix for PNG images with transparency should be applied. Default is false. + * @attribute {String} iepngfix-elements a comma-seperated list of CSS identifiers (classes) to which the transparent-PNG fix will be applied. + * @attribute {Boolean} iphone-fullscreen whether the application should cover the entire screen of the iPhone. Default is true. + * @attribute {String} iphone-statusbar the style of the statusbar of the iPhone webbrowser. Posssible values: 'default', black-translucent' or 'black'. + * @attribute {String} iphone-icon path pointing to the icon that should be used when this application is put on the iPhone Dashboard. + * @attribute {Boolean} iphone-icon-is-glossy whether the icon specified with 'iphone-icon' already is glossy or if the iPhone OS should apply that effect. Default is false. + * @attribute {Boolean} iphone-fixed-viewport whether the viewport of the application is fixed and whether the zoom should be enabled. Default is true. + * @allowchild auth, authentication, offline, printer, defaults + * @todo describe defaults + */ +apf.appsettings = function(struct, tagName){ + this.$init(tagName || "appsettings", apf.NODE_HIDDEN, struct); +}; + +(function(){ + this.$parsePrio = "001"; + + //1 = force no bind rule, 2 = force bind rule + this.$attrExcludePropBind = { + language : 1, + login : 1, + logout : 1 + }; + + this.$supportedProperties = ["debug", "name", "baseurl", "resource-path", + "disable-right-click", "allow-select", "allow-blur", + "auto-disable-actions", "auto-disable", "disable-f5", + "auto-hide-loading", "disable-space", "disable-backspace", "undokeys", + "initdelay", "default-page", "query-append", "outline", "drag-outline", + "resize-outline", "resize-outline", "iepngfix", "iepngfix-elements", + "iphone-fullscreen", "iphone-statusbar", "iphone-icon", + "iphone-icon-is-glossy", "iphone-fixed-viewport", "skinset", + "language", "storage", "offline", "login"]; + this.$booleanProperties = { + + "debug":1, + "disable-right-click":1, + "allow-select":1, + "allow-blur":1, + "auto-disable-actions":1, + "auto-disable":1, + "disable-f5":1, + "auto-hide-loading":1, + "disable-space":1, + "disable-backspace":1, + "undokeys":1, + "initdelay":1, + "outline":1, + "iepngfix":1, + "iphone-fullscreen":1, + "iphone-icon-is-glossy":1, + "iphone-fixed-viewport":1 + }; + + var $setProperty = this.setProperty; + this.setProperty = function(prop, value, forceOnMe, setAttr, inherited){ + if (inherited != 2) + $setProperty.apply(this, arguments); + } + + this.$handlePropSet = function(prop, value, force){ + if (this.$booleanProperties[prop]) + value = apf.isTrue(value); + + this[prop] = value; + + apf.config.setProperty(prop, value); + }; + + this.addEventListener("DOMNodeInsertedIntoDocument", function(e){ + + }); +}).call(apf.appsettings.prototype = new apf.AmlElement()); + +apf.aml.setElement("appsettings", apf.appsettings); + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/elements/audio.js)SIZE(12958)TIME(Fri, 14 Oct 2011 08:01:44 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/elements/auth.js)SIZE(23999)TIME(Fri, 14 Oct 2011 08:01:44 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + + +/** + * @define auth Centralized authentication handling. Not being logged in, after being + * offline for a while can put the application + * in a complex undefined state. The auth element makes sure the state is always + * properly managed. When it gets signalled 'authentication required' it dispatches the + * appropriate events to display a login box. It can automatically retry logging + * in to one or more services using in memory stored username/password + * combinations. It will queue all requests that require authentication until + * the application is logged in again and will then empty the queue. + * Example: + * This example sets up apf.auth with two services that it can log into. + * + * + * + * + * + * + * + * + * Example: + * A login window with different states managed by apf.auth + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * Username + * + * + * Password + * + * + * + * Log in + * + * Log out + * + * + * @event beforelogin Fires before the log in request is sent to the service + * cancelable: Prevents the log in from happening + * @event beforelogout Fires before the log out request is sent to the service + * cancelable: Prevents the log out from happening + * @event logincheck Fires when log in data is received. Login is sometimes very complex, this event is dispatched to allow a custom check if a log in succeeded. + * bubbles: yes + * object: + * {Object} data the data received from the log in request + * {Number} state the return code of the log in request + * @event loginfail Fires when a log in attempt has failed + * @event loginsuccess Fires when a log in attempt succeeded + * @event logoutcheck Fires when log out data is received. Login is sometimes very complex, this event is dispatched to allow a custom check if a log out succeeded. + * bubbles: yes + * object: + * {Object} data the data received from the log out request + * {Number} state the return code of the log out request + * @event logoutfail Fires when a log out attempt has failed + * @event logoutsuccess Fires when a log out attempt succeeded + * @event authrequired Fires when log in credentials are required, either because they are incorrect, or because they are unavailable. + * bubbles: yes + * + * @inherits apf.Class + * + * @attribute {String} login the {@link term.datainstruction data instruction} on how to log in to a service. + * @attribute {String} logout the {@link term.datainstruction data instruction} on how to log out of a service. + * @attribute {Boolean} autostart whether to fire authrequired at startup. Defaults to true. + * @attribute {String} window the id of the window element that offers a log in form to the user. DEPRECATED. + * @attribute {String} authreq-state the id of the state element which is activated when logging in failed because the credentials where incorrect. + * @attribute {String} login-state the id of the state element which is activated when logging in succeeded. + * @attribute {String} waiting-state the id of the state element which is activated when the user is waiting while the application is logging in. + * @attribute {String} fail-state the id of the state element which is activated when logging in failed because the credentials where incorrect. + * @attribute {String} error-state the id of the state element which is activated when logging in failed because of an error (i.e. network disconnected). + * @attribute {String} logout-state the id of the state element which is activated when the user is logged out. + * @attribute {String} model the id of the model element which gets the data loaded given at login success. + * @attribute {String} remember whether to remember the login credentials after the first successful login attempt. Will only be used i.c.w. RPC + * @allowchild service + * @define service Element specifying a server to log into. + * @attribute {String} name the unique identifier of the service + * @attribute {String} login the {@link term.datainstruction data instruction} on how to log in to a service + * @attribute {String} logout the {@link term.datainstruction data instruction} on how to log out of a service + * @see element.appsettings + * + * @default_private + */ + +apf.auth = function(struct, tagName){ + this.$init(tagName || "auth", apf.NODE_HIDDEN, struct); + + this.$services = {}; + this.$cache = {}; + this.$queue = []; + this.$credentials = null; +}; + +apf.aml.setElement("auth", apf.auth); + +(function(){ + this.autostart = true; + this.authenticated = false; + this.enablequeue = false; + + this.$retry = true; + this.loggedIn = false; + this.$needsLogin = false; + this.$hasHost = false; + + /** + * Indicates the state of the log in process. + * Possible values: + * 0 idle + * 1 logging in + * 2 logging out + */ + this.inProcess = 0; + + //1 = force no bind rule, 2 = force bind rule + this.$attrExcludePropBind = apf.extend({ + login : 1, + logout : 1 + }, this.$attrExcludePropBind); + + this.$booleanProperties["autostart"] = true; + this.$booleanProperties["remember"] = true; + + this.$supportedProperties.push("login", "logout", "fail-state", "error-state", + "login-state", "logout-state", "waiting-state", "window", "autostart", + "remember", "authenticated", "enablequeue"); + + this.$propHandlers["login"] = + this.$propHandlers["login-state"] = function(value){ + this.$services["default"] = value ? this : null; + this.$needsLogin = value ? true : false; + }; + + this.register = function(node){ + this.$services[node.name] = node; + this.$needsLogin = true; + }; + + this.unregister = function(node){ + var prop; + delete this.$services[node.name]; + if (!(prop in this.$services)) + this.$needsLogin = false; + }; + + this.addEventListener("DOMNodeInsertedIntoDocument", function(e){ + this.inited = true; + + if (this.parentNode && this.parentNode.$setAuth) { + this.parentNode.$setAuth(this); + this.$hasHost = true; + } + + if (this.autostart && !this.$hasHost) { + var _self = this; + apf.addEventListener("load", function(){ + _self.authRequired(); + apf.removeEventListener("load", arguments.callee); + }); + } + }); + + this.addEventListener("authrequired", function(){ + if (self[this.window]) { + this.win = self[this.window]; + if (this.win) { + this.win.show(); + return false; + } + } + + if (self[this["authreq-state"]]) { + this.state = self[this["authreq-state"]]; + if (this.state) { + this.state.activate(); + return false; + } + } + }); + + this.addEventListener("beforelogin", function(){ + if (self[this["waiting-state"]]) { + this.state = self[this["waiting-state"]]; + if (this.state) + this.state.activate(); + } + }); + + var knownHttpAuthErrors = {401:1, 403:1} + function failFunction(e){ + var st = (e.state == apf.TIMEOUT || !knownHttpAuthErrors[e.status] + ? self[this["error-state"]] + : self[this["fail-state"]]) || self[this["fail-state"]] + + if (st) { + this.state = st; + if (this.state) { + this.state.activate(); + return false; + } + } + } + + this.addEventListener("loginfail", failFunction); + this.addEventListener("logoutfail", failFunction); + + this.addEventListener("logoutsuccess", function(){ + if (self[this["logout-state"]]) { + this.state = self[this["logout-state"]]; + if (this.state) + this.state.activate(); + } + }); + + this.addEventListener("loginsuccess", function(e){ + if (self[this.window]) { + this.win = self[this.window]; + if (this.win) + this.win.hide(); + } + + if (self[this["login-state"]]) { + this.state = self[this["login-state"]]; + if (this.state) + this.state.activate(); + } + + + if (e.data && this.model) { + this.model = apf.nameserver.get("model", this.model); + if (this.model) + this.model.load(e.data); + } + + }); + + /** + * Log in to one or more services + * @param {String} username the username portion of the credentials used to log in with + * @param {String} password the password portion of the credentials used to log in with + * @param {Function} [callback] code to be called when the application succeeds or fails logging in + * @param {Object} [options] extra settings and variables for the login. These variables will be available in the {@link term.datainstruction data instruction} which is called to execute the actual log in. + * Properties: + * {Array} services a list of names of services to be logged in to + * {String} service the name of a single service to log in to + */ + this.logIn = function(username, password, callback, options){ + if (!options) options = {}; + + options.username = username; + options.password = password; + + if (this.dispatchEvent("beforelogin", options) === false) + return false; + + this.inProcess = 1; //Logging in + + var pos = 0, + len = 0, + _self = this, + doneCallback = function() { + if (len != ++pos) + return; + + _self.inProcess = 0; //Idle + _self.loggedIn = true; + _self.clearQueue(); + + if (callback) + callback(); + }; + + if (this.$hasHost) { // child of Teleport element + this.$credentials = options; + callback = this.$hostCallback; + this.$hostCallback = null; + len = 1; + doneCallback(); + this.dispatchEvent("loginsuccess", { + state : 1, + data : null, + bubbles : true, + username : username, + password : password + }); + if (!this.remember) + this.$credentials = null; + } + else { + if (!options.service) { + var s = options.$services || this.$services; + for (var name in s) { + len++; + this.$do(name, options, "in", null, doneCallback); + } + } + else if (options.service) { + len = 1; + this.$do(options.service, options, "in", null, doneCallback); + } + } + }; + + this.relogin = function(){ + if (this.dispatchEvent("beforerelogin") === false) + return false; + + + + //@todo shouldn't I be using inProces here? + var name, pos = 0, len = 0, _self = this, + doneCallback = function(){ + if (len != ++pos) + return; + + _self.inProcess = 0; //Idle + _self.loggedIn = true; + _self.clearQueue(); + }; + + for (name in this.$services) { + if (!this.$cache[name]) + return false; + len++; + this.$do(name, this.$cache[name], "in", true, doneCallback); + } + + return true; + }; + + this.$do = function(service, options, type, isRelogin, callback){ + var xmlNode = this.$services[service], + _self = options.userdata = this; + + + + + + //Execute login call + options.callback = function(data, state, extra){ + if (state == apf.TIMEOUT && extra.retries < apf.maxHttpRetries) + return extra.tpModule.retry(extra.id); + + /* + Login is sometimes very complex, so this check is + here to test the data for login information + */ + var result = _self.dispatchEvent("log" + type + "check", + apf.extend({ + state : state, + data : data, + service : service, + bubbles : true + }, extra)), + + loginFailed = typeof result == "boolean" + ? !result + : !(state == apf.SUCCESS || type == "out" && extra.status == 401); + + if (loginFailed) { + _self.inProcess = 0; //Idle + + if (isRelogin) //If we're retrying then we'll step out here + return _self.authRequired(); + + + + var commError = new Error(apf.formatErrorString(0, null, + "Logging " + type, "Error logging in: " + extra.message)); + + if (_self.dispatchEvent("log" + type + "fail", apf.extend({ + error : commError, + service : service, + state : state, + data : data, + bubbles : true, + username : options.username, + password : options.password + }, extra)) !== false) + throw commError; //@todo ouch, too harsh? + + //@todo Call auth required again?? + + _self.setProperty("authenticated", false); + + return; + } + + if (type == "in") { + //If we use retry, cache the login information + if (!isRelogin && _self.$retry) { + var cacheItem = {}; + for (var prop in options) { + if ("object|array".indexOf(typeof options[prop]) == -1) + cacheItem[prop] = options[prop]; + } + _self.$cache[service || "default"] = cacheItem; + } + } + else { + //Remove cached credentials + if (_self.$cache[service || "default"]) + _self.$cache[service || "default"] = null; + + //_self.authRequired(); + } + + if (callback) + callback(); + + _self.dispatchEvent("log" + type + "success", apf.extend({}, extra, { + state : state, + service : service, + data : data, + bubbles : true, + username : options.username, + password : options.password + })); + + + + _self.setProperty("authenticated", true); + }; + apf.saveData(xmlNode.getAttribute("log" + type), options); + }; + + this.clearQueue = function(){ + if (!this.loggedIn) //Queue should only be cleared when we're logged in + return; + + var queue = this.$queue.slice(); + this.$queue.length = 0; + + for (var i = 0; i < queue.length; i++) { + var qItem = queue[i]; + + //We might be logged out somewhere in this process (think sync) + if (!this.loggedIn) { + this.$queue.push(qItem); + continue; + } + + //Specialty retry (protocol specific) + if (qItem.retry) + qItem.$retry.call(qItem.object); + + //Standard TelePort Module retry + else if (qItem.id) + qItem.tpModule.retry(qItem.id); + + + } + + //The queue might be filled somehow + if (this.$queue.length) + this.clearQueue(); + }; + + /** + * Log out of one or more services + * @param {Function} [callback] code to be called when the application succeeds or fails logging out + * @param {Object} [options] extra settings and variables for the login. These variables will be available out the {@link term.datainstruction data instruction} which is called to execute the actual log out. + * Properties: + * {Array} services a list of names of services to be logged out of + * {String} service the name of a single service to log out of + */ + this.logOut = function(callback, options){ + if (!options) options = {}; + + if (this.dispatchEvent("beforelogout", options) === false) + return; + + this.loggedIn = false; + + if (!options.service) { + for (var name in this.$services) + this.$do(name, options, "out", null, callback); + } + else if (options.service) + this.$do(options.service, options, "out", null, callback); + + }; + + this.getCredentials = function(service){ + var cache = this.$cache[service || "default"]; + return !cache ? ["", ""] : [cache.username, cache.password]; + } + + /** + * Signals services that a log in is required and fires authrequired event + * @param {Object} [options] information on how to reconstruct a failed action, that detected a log in was required. (i.e. When an HTTP call fails with a 401 Auth Required the options object contains information on how to retry the http request) + * @param {Object} [forceNoRetry] don't try to log in with stored credentials. + */ + this.authRequired = function(options, forceNoRetry){ + // If we're already logging in return + if (options && options.userdata == this) + return; + + // If we're supposed to be logged in we'll try to log in automatically + if (this.loggedIn && !forceNoRetry && this.$retry && this.relogin()) { + var result = false; + } + else if (this.inProcess != 1) { //If we're not already logging in + if (this.$hasHost && typeof options == "function") { //inside Teleport element + if (this.$credentials) + return options(); + this.$hostCallback = options; + } + /* + Apparently our credentials aren't valid anymore, + or retry is turned off. If this event returns false + the developer will call apf.auth.login() at a later date. + */ + var result = this.dispatchEvent("authrequired", apf.extend({ + bubbles : true, + data : options && options.data + }, options)); + } + + this.loggedIn = false; + + if (result === false) { + if (this.enablequeue && options) //Add communication to queue for later processing + this.$queue.push(options); + + return true; //cancels error state in protocol + } + }; + +}).call(apf.auth.prototype = new apf.AmlElement()); + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/elements/axis.js)SIZE(14009)TIME(Fri, 14 Oct 2011 08:01:44 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/elements/bar.js)SIZE(4205)TIME(Fri, 14 Oct 2011 08:01:44 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + + +/** + * Element displaying a skinnable rectangle which can contain other + * aml elements. This element is used by other elements such as the + * toolbar and statusbar element to specify sections within those elements + * which in turn can contain other aml elements. + * Remarks: + * This component is used in the accordion element to create its sections. In + * the statusbar the panel element is an alias of bar. + * + * @constructor + * + * @define bar, panel, menubar + * @attribute {String} icon the url pointing to the icon image. + * @attribute {Boolean} collapsed collapse panel on load, default is false + * Possible values: + * true panel is collapsed + * false panel is not collapsed + * @attribute {String} title describes content in panel + * @allowchild button + * @allowchild {elements}, {anyaml} + * @addnode elements + * + * @author Ruben Daniels (ruben AT ajax DOT org) + * @version %I%, %G% + * @since 0.4 + */ +apf.section = function(struct, tagName){ + this.$init(tagName || "section", apf.NODE_VISIBLE, struct); +}; + +apf.menubar = function(struct, tagName){ + this.$init(tagName || "menubar", apf.NODE_VISIBLE, struct); +}; + +apf.bar = function(struct, tagName){ + this.$init(tagName || "bar", apf.NODE_VISIBLE, struct); +}; + +(function(){ + this.$focussable = false; + this.$canLeechSkin = true; + this.$isLeechingSkin = false; + + this.$propHandlers["caption"] = function(value) { + this.$int.innerHTML = value; + } + + //@todo apf3.0 refactor + this.addEventListener("AMLReparent", + function(beforeNode, pNode, withinParent){ + if (!this.$amlLoaded) + return; + + if (this.$isLeechingSkin && !withinParent + && this.skinName != pNode.skinName + || !this.$isLeechingSkin + && this.parentNode.$hasLayoutNode + && this.parentNode.$hasLayoutNode(this.localName)) { + this.$isLeechingSkin = true; + this.$forceSkinChange(this.parentNode.skinName.split(":")[0] + ":" + skinName); + } + }); + + this.$draw = function(){ + //Build Main Skin + this.$ext = this.$getExternal(this.$isLeechingSkin + ? this.localName + : "main"); + + //Draggable area support, mostly for a:toolbar + if (this.oDrag) //Remove if already exist (skin change) + this.oDrag.parentNode.removeChild(this.oDrag); + + this.oDrag = this.$getLayoutNode(this.$isLeechingSkin + ? this.localName + : "main", "dragger", this.$ext); + + this.$int = this.$getLayoutNode(this.$isLeechingSkin + ? this.localName + : "main", "container", this.$ext); + }; + + this.$loadAml = function(x){ + + }; + + + this.$skinchange = function(){ + + } + +}).call(apf.bar.prototype = new apf.Presentation()); + +apf.menubar.prototype = +apf.section.prototype = apf.bar.prototype; + +apf.aml.setElement("bar", apf.bar); +apf.aml.setElement("menubar", apf.menubar); +apf.aml.setElement("section", apf.section); + + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/elements/bindingcolorrule.js)SIZE(2906)TIME(Fri, 14 Oct 2011 08:01:44 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + + +/** + * @todo docs + */ +apf.BindingColorRule = function(struct, tagName){ + this.$init(tagName, apf.NODE_HIDDEN, struct); +}; + +(function(){ + this.addEventListener("DOMNodeInsertedIntoDocument", function(e){ + //@todo This should support multiple color rules, by inserting the rules at the right place. + if (this.$bindings && this.$bindings.color) { + var clr = this.$bindings.color[0]; + apf.setStyleRule("." + this.$baseCSSname + (apf.isIE + ? " .records .highlight SPAN" + : " .records .highlight span"), "color", clr.getAttribute("text"), null, this.oWin); + apf.setStyleRule("." + this.$baseCSSname + (apf.isIE + ? " .records .highlight SPAN" + : " .records .highlight span"), "backgroundColor", clr.getAttribute("row"), null, this.oWin); + apf.setStyleRule("." + this.$baseCSSname + (apf.isIE + ? " .records .highlight" + : " .records .highlight"), "backgroundColor", clr.getAttribute("row"), null, this.oWin); + /*apf.importCssString("." + this.$baseCSSname + " .records div.highlight{background-color:" + + clr.getAttribute("row") + ";} ." + + this.$baseCSSname + " .records div.highlight span{color:" + + clr.getAttribute("text") + ";}");*/ + } + + //"." + this.$baseCSSname + " .headings + apf.importStylesheet([ + ["." + this.className, + "width:" + this.$width + (this.$isPercentage ? "%;" : "px;") + + "text-align:" + h.align], + ["." + this.className, + "width:" + this.$width + (this.$isPercentage ? "%;" : "px;") + + "text-align:" + h.align] + ]); + + this.$draw(); + }); +}).call(apf.BindingColorRule.prototype = new apf.BindingRule()); + +apf.aml.setElement("color", apf.BindingColorRule); + + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/elements/bindingcolumnrule.js)SIZE(21698)TIME(Tue, 13 Dec 2011 13:33:58 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + + +/** + * @attribute {String} icon + * @attribute {String} caption + * @attribute {String} width + * @attribute {String} options + * @attribute {String} editor + * @attribute {String} colspan + * @attribute {String} align + * @attribute {String} css + * @attribute {Boolean} tree + */ +apf.BindingColumnRule = function(struct, tagName){ + this.$init(tagName || "column", apf.NODE_VISIBLE, struct); + + this.$className = "col" + this.$uniqueId; +}; + +(function(){ + this.$defaultwidth = "100"; + this.$width = 0; + + this.$sortable = true; //@todo set defaults based on localName of element to which its applied + this.$resizable = true; + this.$movable = true; + this.$cssInit = false; + + this.visible = true; + + //1 = force no bind rule, 2 = force bind rule + this.$attrExcludePropBind = apf.extend({ + css : 1, + icon : 1, + caption : 1, + eachcaption : 1, + eachvalue : 1, + each : 1, + icon : 1 + }, this.$attrExcludePropBind); + + this.$supportedProperties.push("tree", "icon", "caption", "width", "options", + "check", "editor", "colspan", "align", "css", "sorted", "each", + "eachvalue", "eachcaption", "model"); + + this.$booleanProperties["tree"] = true; + this.$booleanProperties["check"] = true; + this.$booleanProperties["sorted"] = true; + this.$booleanProperties["visible"] = true; + + this.$setParentFixedWidth = function(){ + var pNode = this.parentNode; + var vLeft = (pNode.$fixed) + "px"; + if (!this.$isFixedGrid) { + //apf.setStyleRule("." + this.$baseCSSname + " .headings ." + hFirst.$className, "marginLeft", "-" + vLeft); //Set + //apf.setStyleRule("." + this.$baseCSSname + " .records ." + hFirst.$className, "marginLeft", "-" + vLeft); //Set + apf.setStyleRule("." + pNode.$baseCSSname + " .row" + pNode.$uniqueId, + "paddingRight", vLeft, null, this.oWin); //Set + apf.setStyleRule("." + pNode.$baseCSSname + " .row" + pNode.$uniqueId, + "marginRight", "-" + vLeft, null, pNode.oWin); //Set + + //headings and records have same padding-right + if (pNode.$container) + pNode.$container.style.paddingRight = vLeft; + if (pNode.$head) + pNode.$head.style.paddingRight = vLeft; + } + } + + this.$propHandlers["width"] = function(value, prop){ + if (!value) + return; + + var diff = value - this.$width; + + this.$isPercentage = value && String(value).indexOf("%") > -1; + this.$width = parseFloat(value); + + var pNode = this.parentNode; + + if (this.$isPercentage) { + apf.setStyleRule("." + this.$className, "width", this.$width + "%"); + + //if (apf.z && !this.resizing) + //this.resize(this.$width, pNode, true); + } + else { + apf.setStyleRule("." + this.$className, "width", this.$width + "px", null, pNode.oWin); //Set + + if (pNode.$amlLoaded) { + if (this.visible) + pNode.$fixed += diff; + + this.$setParentFixedWidth(); + } + } + } + + this.$propHandlers["options"] = function(value, prop){ + this.$sortable = value.indexOf("sort") > -1; + this.$resizable = value.indexOf("size") > -1; + this.$movable = value.indexOf("move") > -1; + } + + this.$propHandlers["visible"] = function(value, prop, el, force){ + var pNode = this.parentNode; + + if (!force && !this.$amlLoaded) + return; + + if (value) { + apf.setStyleRule("." + this.$className, + "display", "inline-block", null, this.oWin); + + var size = this.$isPercentage + ? (this.$ext.offsetWidth - (pNode.$widthdiff - 3)) + : this.$width; + + this.resize(size, pNode, true); + } + else { + apf.setStyleRule("." + this.$className, + "display", "none", null, this.oWin); + + this.resize(0, pNode, true); + } + } + + this.resize = function(newsize, pNode, toggleShowHide){ + var hN; + + if (this.$isPercentage) { + var oldsize = (this.visible && this.$ext.offsetWidth + ? this.$ext.offsetWidth - (pNode.$widthdiff - 3) + : 0), + ratio = oldsize ? newsize / oldsize : 1, //div 0 ?? + next = [], + fixed = [], + total = 0, + node = toggleShowHide + ? this.$ext.parentNode.firstChild + : this.$ext.nextSibling; + + while (node && node.getAttribute("hid")) { + hN = apf.all[node.getAttribute("hid")]; + if (hN.visible !== false) { + if (hN.$isPercentage) { + next.push(hN); + total += hN.$width; + } + else fixed.push(hN); + } + node = node.nextSibling; + } + + if (fixed.length && !next.length) + return fixed[0].resize(fixed[0].$width + (oldsize - newsize), pNode); + + var diffPerc, diffRatio; + if (ratio == 1 && total < 101) { + ratio = total/101; + diffRatio = 1/ratio; + } + else if (total > 101) { + ratio = ratio * 101/total + diffRatio = ratio; + } + else { + diffPerc = (ratio - 1) * this.$width; + diffRatio = (total - diffPerc) / total; + if (diffRatio < 0.01 && diffRatio > 0) { + if (newsize < 20) return; + return this.resize(newsize - 10, pNode);//pNode.resizeColumn(nr, newsize - 10); + } + } + + for (var n, i = 0; i < next.length; i++) { + n = next[i]; + if (n == this) + continue; + + n.setProperty("width", String(n.$width * diffRatio) + "%", false, true); + } + + if (this.visible !== false) { + this.setProperty("width", String(ratio * this.$width) + "%", false, true); + } + } + else if (toggleShowHide) { + var diff = newsize; + pNode.$fixed += diff; + this.$setParentFixedWidth(); + } + else { + if (apf.isIE && pNode.oIframe) + this.$ext.style.width = newsize + "px"; + + this.setProperty("width", newsize, false, true); + } + } + + this.hide = function(){ + this.setProperty("visible", false, false, true); + return this; + } + + this.show = function(){ + this.setProperty("visible", true, false, true); + return this; + } + + /** + * Sorts a column. + * @param {Number} hid the heading number; this number is based on the sequence of the column elements. + */ + this.sort = function(pNode, initial){ + if (pNode.$lastSorted == this) { + apf.setStyleClass(this.$int, + pNode.toggleSortOrder() + ? "ascending" + : "descending", ["descending", "ascending"]); + return; + } + + var h; + if (h = pNode.$lastSorted) { + apf.setStyleRule("." + h.$className, "background", "white"); //This breaks row coloring + apf.setStyleClass(h.$int, "", ["descending", "ascending"]); + } + + apf.setStyleRule("." + this.$className, "background", "#f3f3f3"); + apf.setStyleClass(this.$int, "ascending", ["descending", "ascending"]); + + pNode.resort({ + order : "ascending", + xpath : (this.cvalue || this.compile("value")).xpaths[1] + //type : + }, false, initial || !pNode.length); + + + //@todo needs more thought + /*if (pNode.$lastSorted) + pNode.$lastSorted.setProperty("sorted", false); + this.setProperty("sorted", true);*/ + + pNode.$lastSorted = this; + }; + + /** + * Moves a column to another position. + * @param {Number} fromHid the heading number of the column to move; this number is based on the sequence of the column elements. + * @param {Number} toHid the position the column is moved to; + */ + this.move = function(hTo, pNode){ + if (hTo && this == hTo) + return; + + var hFrom = this, + childNrFrom = apf.getChildNumber(hFrom.$int), + childNrTo = hTo && apf.getChildNumber(hTo.$int); + + pNode.$head.insertBefore(hFrom.$int, hTo && hTo.$int || null); + + if (!pNode.length) + return; + + (function _recur(nodes){ + for (var node, i = 0; i < nodes.length; i++) { + if (nodes[i].nodeType != 1) + continue; + //if (pNode.$withContainer && ((i+1) % 2) == 0) + //continue; + + node = nodes[i]; + if (pNode.$isTreeArch && node.tagName == "BLOCKQUOTE") { //@todo small hack + _recur(node.childNodes); + } + else { + node.insertBefore(node.childNodes[childNrFrom], + childNrTo != undefined && node.childNodes[childNrTo] || null); + } + } + })(pNode.$container.childNodes); + + /*if (this.$first == from || this.$first == to) { + var hReset = this.$first == from ? hFrom : hTo; + + apf.setStyleRule("." + this.$baseCSSname + " .headings ." + + hReset.className, "marginLeft", "-5px"); //Reset + apf.setStyleRule("." + this.$baseCSSname + " .records ." + + hReset.className, "marginLeft", "-5px"); //Reset + + this.$first = pNode.$head.firstChild.getAttribute("hid"); + var h = headings[this.$first]; + var vLeft = "-" + (this.$fixed + 5) + "px"; + + apf.setStyleRule("." + this.$baseCSSname + " .headings ." + + h.className, "marginLeft", vLeft); //Set + apf.setStyleRule("." + this.$baseCSSname + " .records ." + + h.className, "marginLeft", vLeft); //Set + }*/ + } + + this.$draw = function(pNode, caption, width, className){ + //Find the parent this rule works on + var pNode = pNode || this.parentNode; + while (pNode.$bindingRule) + pNode = pNode.parentNode; + + if (!pNode.hasFeature(apf.__PRESENTATION__)) + return; + + if (width) + this.$propHandlers["width"].call(this, width); + + //"." + this.$baseCSSname + " .headings + //if initial + //only needs once if this works + + apf.importStylesheet([ + ["." + this.$className, + "width:" + this.$width + (this.$isPercentage ? "%;" : "px;") + + "text-align:" + this.align + ";display: inline-block"], + ]); + + //Add to htmlRoot + pNode.$getNewContext("headitem"); + var $head = pNode.$getLayoutNode("headitem"); + $head.setAttribute("class", this.$className + (className ? " " + className : "")); + $head.setAttribute("hid", this.$uniqueId); + + var hCaption = pNode.$getLayoutNode("headitem", "caption"); + /*if (this.icon) { + this.$sortable = false; + $head.setAttribute("style", "background-image:url(" + + apf.getAbsolutePath(pNode.iconPath, this.icon) + + ")"); + hCaption.nodeValue = " "; + } + else*/ + hCaption.nodeValue = this.caption || caption || ' '; + + this.$ext = this.$int = apf.insertHtmlNode($head, pNode.$head || pNode.$container); + + var dragging = false; + var _self = this; + + if (this.sorted) + this.sort(pNode, true); + + /*this.$int.onmouseover = function(e){ + if (!e) e = event; + + if (pNode.disabled) return; + + clearTimeout(this.$timer); + + apf.setStyleClass(this, "hover", ["down"]); + };*/ + + this.$int.onmouseup = function(e){ + if (!e) e = event; + + if (pNode.disabled || !apf.isChildOf(dragging, this, true)) + return; + + apf.setStyleClass(this, "hover", ["down"]); + + if (_self.$sortable) + _self.sort(pNode); + + //@todo pNode or Self? + pNode.dispatchEvent("sortcolumn", _self); + }; + + this.$int.onmousedown = function(e){ + if (!e) e = event; + dragging = target = this; + + if (pNode.disabled) return; + + //Resizing + var pos = apf.getAbsolutePosition(target), + sLeft = pNode.$head.scrollLeft; + var d = e.clientX - pos[0] + sLeft; + if (d < 4 || target.offsetWidth - d - 8 < 3 + && apf.getChildNumber(_self.$int) < pNode.$headings.length - 1) { + var t = d < 4 && target.previousSibling || target; + + if (_self.$resizable) { + pos = apf.getAbsolutePosition(t); + apf.setStyleClass(pNode.$pointer, "size_pointer", ["move_pointer"]); + pNode.$pointer.style.display = "block"; + pNode.$pointer.style.left = (t.offsetLeft - sLeft - 1) + "px"; + pNode.$pointer.style.width = (t.offsetWidth - pNode.$widthdiff + 1) + "px"; + + + apf.plane.show(pNode.$pointer, null, true); + + + dragging = true; + document.onmouseup = function(){ + if (!e) e = event; + + document.onmouseup = + document.onmousemove = null; + + apf.all[t.getAttribute("hid")].resize(pNode.$pointer.offsetWidth, pNode); + + dragging = false; + pNode.$pointer.style.display = "none"; + + + apf.plane.hide(); + + + }; + + document.onmousemove = function(e){ + if (!e) e = event; + + pNode.$pointer.style.width = Math.max(10, + Math.min(pNode.$container.offsetWidth - pNode.$pointer.offsetLeft - 20, + e.clientX - pos[0] - 1 + sLeft)) + "px"; + }; + + return; + } + } + + apf.setStyleClass(target, "down", ["hover"]); + + //Moving + if (!_self.$movable) { + document.onmouseup = function(e){ + document.onmouseup = null; + dragging = false; + }; + + return; + } + + apf.setStyleClass(pNode.$pointer, "move_pointer", ["size_pointer"]); + + var x = e.clientX - target.offsetLeft, sX = e.clientX, + y = e.clientY - target.offsetTop, sY = e.clientY, + copy; + + document.onmouseup = function(e){ + if (!e) e = event; + + document.onmouseup = + document.onmousemove = null; + + dragging = false; + pNode.$pointer.style.display = "none"; + + if (!copy) + return; + + copy.style.top = "-100px"; + + var el = document.elementFromPoint(e.clientX, e.clientY); + if (el.parentNode == copy.parentNode) { + var pos = apf.getAbsolutePosition(el); + var beforeNode = (e.clientX - pos[0] > el.offsetWidth / 2 + ? el.nextSibling + : el); + + _self.move(beforeNode ? apf.all[beforeNode.getAttribute("hid")] : null, pNode); + } + + apf.destroyHtmlNode(copy); + }; + + document.onmousemove = function(e){ + if (!e) e = event; + + if (!copy) { + if (Math.abs(e.clientX - sX) < 3 && Math.abs(e.clientY - sY) < 3) + return; + + copy = target.cloneNode(true); + copy.style.position = "absolute"; + var diff = apf.getWidthDiff(target); + copy.style.width = (target.offsetWidth - diff + - pNode.$widthdiff + 2) + "px"; + copy.style.left = target.offsetLeft; + copy.style.top = target.offsetTop; + copy.style.margin = 0; + copy.removeAttribute("hid") + + apf.setStyleClass(copy, "drag", ["ascending", "descending"]); + target.parentNode.appendChild(copy); + } + + copy.style.top = "-100px"; + pNode.$pointer.style.display = "none"; + + var el = document.elementFromPoint(e.clientX, e.clientY); + if (el.parentNode == copy.parentNode) { + var pos = apf.getAbsolutePosition(el); + pNode.$pointer.style.left = (el.offsetLeft + + ((e.clientX - pos[0] > el.offsetWidth / 2) + ? el.offsetWidth - 8 + : 0)) + "px"; + pNode.$pointer.style.display = "block"; + } + + copy.style.left = (e.clientX - x) + 'px'; + copy.style.top = (e.clientY - y) + 'px'; + }; + }; + + this.$int.onmouseout = function(e){ + if (!e) e = event; + + if (pNode.disabled) + return; + + var _self = this; + this.$timer = setTimeout(function(){ + pNode.$ext.style.cursor = ""; + apf.setStyleClass(_self, "", ["hover", "down"]); + }, 10); + }; + + this.$int.onmousemove = function(e){ + if (dragging || pNode.disabled) + return; + + if (!e) e = event; + + var pos = apf.getAbsolutePosition(this), + sLeft = pNode.$head.scrollLeft; + var d = e.clientX - pos[0] + sLeft; + + if (d < 4 || this.offsetWidth - d - pNode.$widthdiff < 3 + && apf.getChildNumber(_self.$int) < pNode.$headings.length - 1) { + var t = d < 4 ? this.previousSibling : this; + pNode.$ext.style.cursor = t && _self.$resizable + ? (apf.isWebkit ? "ew-resize" : "w-resize") + : "default"; + + apf.setStyleClass(this, "", ["hover", "down"]); + } + else { + pNode.$ext.style.cursor = "default"; + apf.setStyleClass(this, "hover", ["down"]); + } + }; + + if (!this.options && pNode.options) + this.$propHandlers["options"].call(this, + this.options = pNode.options); + + if (this.visible === false) + this.$propHandlers["visible"].call(this, false, null, null, true); + + return this; + } + + this.addEventListener("DOMNodeInsertedIntoDocument", function(e){ + this.$draw(); + }); + + this.addEventListener("DOMNodeRemovedFromDocument", function(e){ + if (!this.$int) + return; + + this.$int.onmouseover = + this.$int.onmouseup = + this.$int.onmousedown = + this.$int.onmousemove = + this.$int.onmouseout = null; + }); + +}).call(apf.BindingColumnRule.prototype = new apf.BindingRule()); + +apf.aml.setElement("column", apf.BindingColumnRule); + + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/elements/bindingdndrule.js)SIZE(3623)TIME(Tue, 13 Dec 2011 13:33:58 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + + +/** + * @attribute {String} target + * @attribute {String} action + * @attribute {Boolean} copy + */ +apf.BindingDndRule = function(struct, tagName){ + this.$init(tagName, apf.NODE_HIDDEN, struct); +}; + +(function(){ + this.compile = function(prop){ + if (!this[prop]) + return; + + var compileData; + if (prop == "value") + compileData = apf.lm.compile(this[prop], { + xpathmode : 3 + }); + else if (prop == "match") + compileData = apf.lm.compile(this[prop], { + xpathmode : 3, + injectself : true + }); + else if (prop == "target") + compileData = apf.lm.compile(this[prop], { + xpathmode : 2, + injectself : true + }); + else if (prop == "action") + compileData = apf.lm.compile(this[prop], { + nostring : true + }); + else if (prop == "copy") + compileData = apf.lm.compile(this[prop], { + withopt : true, + nostring : true + }); + else + throw new Error("Missing property handler for compile"); + + return (this["c" + prop] = compileData); + } + + //1 = force no bind rule, 2 = force bind rule + this.$attrExcludePropBind = apf.extend({ + target : 1, + parent : 1, + action : 1, + copy : 1 + }, this.$attrExcludePropBind); + + this.$propHandlers["target"] = + this.$propHandlers["parent"] = + this.$propHandlers["action"] = + this.$propHandlers["copy"] = function(value, prop){ + delete this["c" + prop]; + } + + this.$noderegister = function(e){ + apf.GuiElement.propHandlers["drop"].call(e.amlNode, true); + } + + //@todo removal + + this.addEventListener("DOMNodeInsertedIntoDocument", function(e){ + //Find parent that this rule works on + var pNode = this; + while (pNode && pNode.$bindingRule) + pNode = pNode.parentNode; + + if (!pNode) + return; + + if (pNode.localName == "bindings") { + pNode.addEventListener("noderegister", this.$noderegister); + + var nodes = pNode.$amlNodes; + for (var i = 0; i < nodes.length; i++) + apf.GuiElement.propHandlers["drop"].call(nodes[i], true); + } + else { + apf.GuiElement.propHandlers["drop"].call(pNode, true); + } + }); +}).call(apf.BindingDndRule.prototype = new apf.BindingRule()); + +apf.aml.setElement("drag", apf.BindingDndRule); +apf.aml.setElement("drop", apf.BindingDndRule); + + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/elements/bindingeachrule.js)SIZE(11503)TIME(Fri, 14 Oct 2011 08:01:44 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + + +/** + * @binding each Determines the list of elements for which each + * gets a visual representation within the element. It also can determine + * the sequence of how the elements are visualized by offering a way to + * specify the sort order. (N.B. The sorting mechanism is very similar to + * that of XSLT) + * Example: + * This example contains a list that displays elements with the tagName + * 'mail' that do not have a deleted attribute set to 1. + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * Example: + * This example shows how to use the each rule to order files based + * on their modified data. + * + * + * + * + * + * + * + * + * + * + * + * + * + * Example: + * This example shows how to do complex sorting using a javascript callback function. + * + * + * + * + * + * + * + * + * + * + * + * + * function sth_compare(value, args, xmlNode) { + * + * } + * + * + * + * + * + * + * @attribute {String} match an xpath statement which selects the nodes + * which will be rendered. + * @attribute {String} sort an xpath statement which selects the value + * which is subject to the sorting algorithm. + * @attribute {String} data-type the way sorting is executed. See + * {@link baseclass.multiselectbinding.binding.each.attribute.sort-method} + * on how to specify a custom sort method. + * Possible values: + * string Sorts alphabetically + * number Sorts based on numerical value (i.e. 9 is lower than 10). + * date Sorts based on the date sequence (21-6-1980 is lower than 1-1-2000). + * See {@link baseclass.multiselectbinding.binding.each.attribute.date-format} + * on how to specify the date format. + * @attribute {String} date-format the format of the date on which is sorted. + * Possible values: + * YYYY Full year + * YY Short year + * DD Day of month + * MM Month + * hh Hours + * mm Minutes + * ss Seconds + * Example: + * + * date-format="DD-MM-YYYY" + * + * @attribute {String} sort-method the name of a javascript function to executed + * to determine the value to sort on. + * @attribute {String} order the order of the sorted list. + * Possible values: + * ascending Default sorting order + * descending Reverses the default sorting order. + * @attribute {String} case-order whether upper case characters have preference + * above lower case characters. + * Possible values: + * upper-first Upper case characters are higher. + * lower-first Lower case characters are higher. + */ +apf.BindingEachRule = function(struct, tagName){ + this.$init(tagName, apf.NODE_HIDDEN, struct); + + var _self = this; + this.$noderegister = function(e){ + e.amlNode.$handleBindingRule(_self.match, "each"); + + + e.amlNode.$sort = _self.sort ? new apf.Sort(_self) : null; + + } +}; + +(function(){ + //1 = force no bind rule, 2 = force bind rule + this.$attrExcludePropBind = apf.extend({ + "sort" : 1, + "data-type" : 1, + "date-format" : 1, + "sort-method" : 1, + "order" : 1, + "case-order" : 1 + }, this.$attrExcludePropBind); + + this.$booleanProperties["animate"] = true; + + this.$propHandlers["sort"] = + this.$propHandlers["data-type"] = + this.$propHandlers["date-format"] = + this.$propHandlers["order"] = + this.$propHandlers["case-order"] = function(value, prop){ + delete this["c" + prop]; + + //@todo apf3.0 change sort + } + + this.$updateEach = function(value){ + var pNode = this.parentNode;//@todo apf3.0 get a list via $bindings + if (pNode.localName == "bindings") { + var nodes = pNode.$amlNodes, + i = 0, + l = nodes.length; + for (; i < l; ++i) + if (nodes[i].each != value) + nodes[i].$handleBindingRule(value, "each"); + } + else + if (pNode.each != value) + pNode.$handleBindingRule(value, "each"); + } + + this.$getFilteredMatch = function(value){ + if (!value) + return this.match; + + var keywords = value.trim().toLowerCase().split(" "); + + var each = this.match.charAt(0) == "[" && this.match.charAt(this.match.length - 1) == "]" + ? this.match.replace(/^\[|\]$/g, "") + : this.match; + + var model; + if (each.indexOf("::") > -1) { + var parsed = each.split("::"); //@todo could be optimized + if (!apf.xPathAxis[parsed[0]]) { + model = parsed[0]; + each = parsed[1]; + } + } + + // show search results + var search = [], word, words, fields = this.$fields || ["text()"]; + for (var i = 0, l = keywords.length; i < l; i++) { + words = [], word = keywords[i]; + for (var j = 0, jl = fields.length; j < jl; j++) { + words.push("contains(translate(" + fields[j] + ", 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz'), '" + word + "')"); + } + search.push(words.join(" or ")); + } + + var filter = "(" + search.join(") and (") + ")"; + var groups = this["filter-groups"] ? this["filter-groups"].split("|") : []; + + each = each.split("|"); + var newEach = []; + for (i = 0, l = each.length; i < l; i++) { + if (!groups.contains(each[i])) + newEach.push(each[i] + "[" + filter + "]"); + } + + var subEach = newEach.join("|"); + + for (i = 0; i < groups.length; i++) { + newEach.push(groups[i] + "[" + subEach + "]"); + } + + return newEach.join("|"); + } + + /** + * + */ + + this.$propHandlers["filter"] = function(value, prop){ + if (!this.$amlLoaded) + return; + + this.$updateEach(this.$getFilteredMatch(value)); + } + this.$propHandlers["filter-fields"] = function(value, prop){ + this.$fields = value.splitSafe(","); + } + + this.addEventListener("prop.match", function(e){ + if (!this.$amlLoaded) + return; + + if (this.filter) + this.$propHandlers["filter"].call(this, this.filter); + else + this.$updateEach(this.match); + }); + + //@todo apf3.0 optimize + var f; + this.addEventListener("DOMNodeInserted", f = function(e){ + if (e.currentTarget != this) + return; + + var match = this.$getFilteredMatch(this.filter); + var pNode = this.parentNode;//@todo apf3.0 get a list via $bindings + if (pNode.localName == "bindings") { + pNode.addEventListener("noderegister", this.$noderegister); + + var nodes = pNode.$amlNodes, + i = 0, + l = nodes.length; + for (; i < l; ++i) { + nodes[i].$handleBindingRule(match, "each"); + + + nodes[i].$sort = this.sort ? new apf.Sort(this) : null; + + } + } + else { + pNode.$handleBindingRule(match, "each"); + + + pNode.$sort = this.sort ? new apf.Sort(this) : null; + + } + }); + + this.addEventListener("DOMNodeRemoved", function(e){ + if (e.currentTarget != this) + return; + + //@todo apf3.0 how does this conflict with setting it through an attribute. + //this.$clearDynamicProperty("each"); + //pNode.setProperty("each", null);//@todo double? + //@todo remove model? + + //@todo this should be near $handleBindingRule... + var pNode = this.parentNode;//@todo apf3.0 get a list via $bindings + if (pNode.localName == "bindings") { + pNode.removeEventListener("noderegister", this.$noderegister); + + var nodes = pNode.$amlNodes, + i = 0, + l = nodes.length; + for (; i < l; ++i) { + //delete nodes[i].each; //@todo apf3.x is already set by new one + + + delete nodes[i].$sort; + + } + } + else { + //delete pNode.each; //@todo apf3.x is already set by new one + + + delete pNode.$sort; + + } + }); + + this.addEventListener("DOMNodeInsertedIntoDocument", f); + +}).call(apf.BindingEachRule.prototype = new apf.BindingRule()); + +apf.aml.setElement("each", apf.BindingEachRule); + + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/elements/bindingloadrule.js)SIZE(1529)TIME(Fri, 14 Oct 2011 08:01:44 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + + +/** + * @todo docs + */ +apf.BindingLoadRule = function(struct, tagName){ + this.$init(tagName, apf.NODE_HIDDEN, struct); +}; + +(function(){ + //1 = force no bind rule, 2 = force bind rule + this.$attrExcludePropBind = apf.extend({ + get : 1 + }, this.$attrExcludePropBind); + + this.$propHandlers["get"] = function(value, prop){ + delete this["c" + prop]; + } +}).call(apf.BindingLoadRule.prototype = new apf.BindingRule()); + +apf.aml.setElement("load", apf.BindingLoadRule); +apf.aml.setElement("insert", apf.BindingLoadRule); + + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/elements/bindingquicksandrule.js)SIZE(12333)TIME(Fri, 14 Oct 2011 08:01:44 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + + +/** + * @todo docs + */ +apf.BindingQuicksandRule = function(struct, tagName){ + this.$init(tagName, apf.NODE_HIDDEN, struct); +}; + +(function(){ + function getFilteredNodes(pNode) { + pNode = pNode || this.$parent; + var attr, tmp, + res = [], + dataIDs = [], + xmlNodes = pNode.$getDataNode("quicksand", pNode.xmlRoot, false, null, true), + domNodes = pNode.$ext.childNodes, + i = 0, + l = xmlNodes.length; + if (!domNodes.length || !l) + return res; + + for (; i < l; ++i) + dataIDs.push(xmlNodes[i].getAttribute("a_id")); + + for (i = 0, l = domNodes.length; i < l; ++i) { + if (!domNodes[i] || !(attr = domNodes[i].getAttribute("id"))) + continue; + tmp = attr.split("|"); + tmp.pop(); + if (dataIDs.indexOf(tmp.join("|")) > -1) + res.push(domNodes[i]); + } + + return res; + } + + function nodeInFilter(node, filtered) { + var i = 0, + l = filtered.length, + s = node.getAttribute("id"); + for (; i < l; ++i) { + if (filtered[i].getAttribute("id") == s) + return filtered[i]; + } + return null; + } + + function quicksand(parent, customOptions) { + parent = this.$parent.$ext; + + var i, l, dest, src, offset, + _self = this, + coll = getFilteredNodes.call(this), // destination (target) collection + //sourceHeight = apf.getStyle(parent, "height"), // used to keep height and document flow during the animation + parentOffset = apf.getAbsolutePosition(parent), // offset of visible container, used in animation calculations + offsets = [], // coordinates of every source collection item + callbackFunction = typeof(arguments[1]) == "function" + ? arguments[1] + : typeof arguments[2] == "function" ? arguments[2] : apf.K, + // Gets called when any animation is finished + //var postCallbackPerformed = 0; // prevents the function from being called more than one time + postCallback = function() { + _self.dispatchEvent("afterquicksand"); + callbackFunction.call(_self); + /*if (!postCallbackPerformed) { + parent.innerHTML = $dest.innerHTML; // put target HTML into visible source container + $("[data-quicksand-owner=" + cssPath(parent) + "]").remove(); // remove all temporary containers + if (typeof callbackFunction == "function") + callbackFunction.call(this); + postCallbackPerformed = 1; + }*/ + }, + options = { + steps: 60, + anim: apf.tween.easeInOutQuad, + onfinish: postCallback, + // put false if you don't want the plugin to adjust height of container to fit all the items + adjustHeight: true + }; + apf.extend(options, customOptions);; + + // Replace the collection and quit if IE6 + if (apf.isIE && apf.isIE < 7) { + parent.innerHTML = ""; + for (i = 0, l = coll.length; i < l; i++) + parent.appendChild(coll[i]); + return; + } + + var $source = parent.childNodes; // source collection items + + // Position: relative situations + var correctionOffset = [0, 0]; + /*var $correctionParent = parent.offsetParent(); + var correctionOffset = apf.getAbsolutePosition($correctionParent); + if (apf.getStyle($correctionParent, "position") == "relative") { + if ($correctionParent.get(0).nodeName.toLowerCase() == "body") { + + } + else { + correctionOffset[0] += parseFloat($correctionParent.css("border-top-width")); + correctionOffset[1] += parseFloat($correctionParent.css("border-left-width")); + } + } else { + correctionOffset[0] -= parseFloat($correctionParent.css("border-top-width")); + correctionOffset[1] -= parseFloat($correctionParent.css("border-left-width")); + correctionOffset[0] -= parseFloat($correctionParent.css("margin-top")); + correctionOffset[1] -= parseFloat($correctionParent.css("margin-left")); + }*/ + + + // keeps nodes after source container, holding their position + parent.style.height = parent.offsetHeight + "px"; + + // stops previous animations on source container + apf.tween.clearQueue(parent, true);//$(this).stop(); + + // get positions of source collections + for (i = 0, l = $source.length; i < l; ++i) { + src = $source[i]; + offsets[i] = apf.getAbsolutePosition(src, parent, false); + } + for (i = 0, l = $source.length; i < l; ++i) { + src = $source[i]; + // This doesn"t move any element at all, just sets position to absolute + // and adjusts top & left to make them compatible with position: absolute + with (src.style) { + position = "absolute"; + margin = "0"; + top = (offsets[i][1] - parentOffset[1]) + "px";// - parseFloat(apf.getStyle(src, "margin-top")) + "px"; + left = (offsets[i][0] - parentOffset[0]) + "px";// - parseFloat(apf.getStyle(src, "margin-left")) + "px"; + } + } + + // create temporary container with destination collection + var $dest = parent.cloneNode(false); + $dest.setAttribute("id", ""); + //$dest.setAttribute("data-quicksand-owner", parent.selector); + //$dest.style.height = "auto"; + //$dest.style.width = parent.offsetWidth + "px"; + + for (i = 0, l = coll.length; i < l; ++i) { + $dest.appendChild(coll[i].cloneNode(true)); + coll[i].$offset = apf.getAbsolutePosition(coll[i]); + } + // insert node into HTML + // Note that the node is under visible source container in the exactly same position + // The browser render all the items without showing them (opacity: 0.0) + // No offset calculations are needed, the browser just extracts position from underlayered destination items + // and sets animation to destination positions. + parent.parentNode.insertBefore($dest, parent); + //$dest.setAttribute("data-quicksand-owner", cssPath(parent)); + with ($dest.style) { + zIndex = 1; + opacity = 0; + margin = 0; + position = "absolute"; + top = parentOffset[1] - correctionOffset[1] + "px"; + left = parentOffset[0] - correctionOffset[0] + "px"; + } + + // If destination container has different height than source container + // the height can be animated, adjusting it to destination height + if (false) {//options.adjustHeight) { + apf.tween.single(parent, { + steps: options.steps, + anim : options.anim, + type : "height", + from : parent.offsetHeight, + to : $dest.offsetHeight + });//parent.animate({height: $dest.height()}, options.duration, options.easing); + } + + // Now it's time to do shuffling animation + // First of all, we need to identify same elements within source and destination collections + var insets = []; + for (i = 0, l = $source.length; i < l; ++i) { + src = $source[i]; + //var destElement = coll.filter("[" + options.attribute + "=" + src.getAttribute(options.attribute) + "]"); + if (dest = nodeInFilter(src, coll)) {//destElement.length) { + // The item is both in source and destination collections + // If it's under different position, let's move it + offset = insets.shift() || offsets[i]; + options.tweens = [{ + type: "top", + from: dest.$offset[1] - parentOffset[1], + to : offset[1] - parentOffset[1] + }, { + type: "left", + from: dest.$offset[0] - parentOffset[0], + to : offset[0] - parentOffset[0] + }, { + type: "opacity", + from: apf.getStyle(src, "opacity"), + to : 1 + }]; + if (apf.supportCSSAnim) { + options.tweens.push({ + type: "transform", + subType: "scale", + from: 0, + to: 1.0 + }); + } + apf.tween.multi(src, options); + } + else { + // The item from source collection is not present in destination collections + // Let's remove it + options.tweens = [{ + type: "opacity", + from: apf.getStyle(src, "opacity"), + to : 0 + }]; + if (apf.supportCSSAnim) { + options.tweens.push({ + type: "transform", + subType: "scale", + from: 1.0, + to: 0 + }); + } + insets.push(offsets[i]); + apf.tween.multi(src, options); + } + } + + for (i = 0, l = coll.length; i < l; ++i) { + var item = coll[i]; + // Grab all items from target collection not present in visible source collection + if (!nodeInFilter(item, $source)) { + // No such element in source collection... + dest = nodeInFilter(item, coll); + + options.tweens = [{ + type: "opacity", + from: 0, + to : 1 + }]; + if (!apf.isIE) { + // @todo add scaleTo animation (CSS3) to 0.0 + } + + // Let's create it + offset = apf.getAbsolutePosition(dest); + dest = dest.cloneNode(true); + + with (dest.style) { + position = "absolute"; + margin = "0"; + top = (offset[1] - parentOffset[1]) + "px"; + left = (offset[0] - parentOffset[0]) + "px"; + opacity = "0"; + transform = "scale(.0)"; + } + parent.appendChild(dest); + apf.tween.multi(dest, options); + } + } + } + + this.addEventListener("DOMNodeInsertedIntoDocument", function(e){ + //Find parent that this rule works on + var pNode = this; + while (pNode && pNode.$bindingRule) + pNode = pNode.parentNode; + this.$parent = pNode; + + if (!pNode) + return; + + var _self = this; + setTimeout(function() { + quicksand.call(_self); + }); + }); +}).call(apf.BindingQuicksandRule.prototype = new apf.BindingRule()); + +apf.aml.setElement("quicksand", apf.BindingQuicksandRule); + + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/elements/bindings.js)SIZE(8618)TIME(Fri, 14 Oct 2011 08:01:44 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + + +/** + * @define bindings element containing all the binding rules for the data + * bound elements referencing this element. + * Example: + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * @see element.smartbinding + * + * @constructor + * @apfclass + * + * @author Ruben Daniels (ruben AT ajax DOT org) + * @version %I%, %G% + * @since 0.8 + * + * @default_private + */ +apf.bindings = function(struct, tagName){ + this.$init(tagName || "bindings", apf.NODE_HIDDEN, struct); + + this.$bindings = new apf.ruleList(); + this.$amlNodes = {}; +}; + +(function(){ + this.$smartbinding = null; + + this.register = function(amlNode){ + if (amlNode.localName == "smartbinding") { + this.$smartbinding = amlNode; + this.$smartbinding.add(this); //Assuming only at init + return; + } + + if (!amlNode.hasFeature(apf.__DATABINDING__)) + return; + + this.$amlNodes[amlNode.$uniqueId] = amlNode; + + if (!this.$amlLoaded) + return; + + if (!this.$bindings.$isCompiled) + this.$cbindings = this.$bindings.compile(); + + amlNode.$bindings = this.$bindings; + amlNode.$cbindings = this.$cbindings; + amlNode.$bindingsElement = this; + + //@todo apf3.0 should be deprecated + amlNode.dispatchEvent("bindingsload", { + bindings: this.$bindings, + compiled: this.$cbindings + }); + this.dispatchEvent("noderegister", { + amlNode: amlNode + }); + amlNode.$checkLoadQueue(); + }; + + this.unregister = function(amlNode){ + //unregister element + this.$amlNodes[amlNode.$uniqueId] = null; + delete this.$amlNodes[amlNode.$uniqueId]; + + amlNode.$bindingsElement = + amlNode.$bindings = + amlNode.$cbindings = false; + + amlNode.dispatchEvent("bindingsunload", { + bindings: this.$bindings, + compiled: this.$cbindings + }); + }; + + this.reload = function(){ + for (var id in this.$amlNodes){ + this.$amlNodes[id].reload(); + } + } + + /**** DOM Handlers ****/ + + this.addEventListener("DOMNodeInsertedIntoDocument", function(e){ + var nodes = this.childNodes; + for (var node, i = 0, l = nodes.length; i < l; i++) { + if (!(node = nodes[i]).$amlLoaded && node.nodeType == 1) + node.dispatchEvent("DOMNodeInsertedIntoDocument"); //{relatedParent : nodes[j].parentNode} + } + + this.register(this.parentNode); + + for (var id in this.$amlNodes) + this.register(this.$amlNodes[id]); + }); +}).call(apf.bindings.prototype = new apf.AmlElement()); + +apf.ruleList = function(){ + this.$compiled = {}; +} +apf.ruleList.prototype = { + $isCompiled : false, + + getRule : function(name, xmlNode){ + var rules = this[name]; + if (!rules) return false; + + //@todo Shouldn't allow async calls..., should always give a function + for (var func, rule, i = 0, l = rules.length; i < l; i++) { + rule = rules[i]; + if (!rule.match) + return rule; + + func = rule.cmatch || rule.compile("match", {injectself: true, xpathmode: 2}); + if (func && func(xmlNode)) + return rule; + } + }, + + compile : function(name){ + var rules, s, c = this.$compiled, hasAml = false; + + if (name) { + s = []; + rules = this[name]; + for (var rule, i = 0, l = rules.length; i < l; i++) { + if (!(rule = rules[i]).match && !rule.value) + continue; + + s.push(rule.match, rule.value); + + if (!hasAml && rule.value) + hasAml = rule.hasaml || rule.value.indexOf(" -1; + + } + + //always give a function, no async calls (could also just error on execution) + c[name] = apf.lm.compileMatch(s); + + + c[name].hasAml = hasAml; + + + return c; + } + + for (name in this) { + if (name == "each") + continue; + + rules = this[name]; + if (rules.dataType != apf.ARRAY) + continue; + + s = [], hasAml = false; + for (var rule, i = 0, l = rules.length; i < l; i++) { + if (!(rule = rules[i]).match && !rule.value) + continue; + + s.push(rule.match, rule.value); + + if (!hasAml && rule.value) + hasAml = rule.hasaml || rule.value.indexOf(" -1; + + } + + //always give a function, no async calls (could also just error on execution) + c[name] = apf.lm.compileMatch(s); + + + c[name].hasAml = hasAml; + + } + + this.$isCompiled = true; + + return c; + }, + + getRuleIndex : function(name, index) { + var rule = this[name][index]; + if (rule.value) { + if (!rule.cvalue) + rule.compile("value"); + } + else if (rule.match) { + if (!rule.cmatch) + rule.compile("match"); + } + return rule; + }, + + getDataNode : function(name, xmlNode, createNode, ruleList, multiple){ + var i, l, func, node, rule, rules = this[name]; + if (!rules) + return; + + //@todo Shouldn't allow async calls..., should always give a function + for (rule, i = 0, l = rules.length; i < l; i++) { + rule = rules[i]; + + func = rule.cvaluematch; + if (!func) { //@todo apf3.0 cleanup + if (rule.match && rule.value && rule.value.match(/^\[.*\]$/)) + rule.valuematch = "{_n = " + rule.match + "; %[child::" + + rule.value.substr(1, rule.value.length - 2) + .split("|").join("|child::") + "]}"; + else + rule.valuematch = rule.match || rule.value; + + func = rule.$compile("valuematch", { + xpathmode : multiple ? 4 : 3, + injectself : rule.match ? true : false + }); + } + + if (func && (node = func(xmlNode, createNode))) { + if (ruleList) + ruleList.push(rule); + + return node; + } + } + + return false; + } +} + +apf.aml.setElement("bindings", apf.bindings); + + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/elements/bindingseriesrule.js)SIZE(1944)TIME(Fri, 14 Oct 2011 08:01:44 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + + +/** + * @attribute {String} formula + * @attribute {Number} length + * @attribute {String} delimiter + * @attribute {String} split + * @attribute {String} css + */ +apf.BindingSeriesRule = function(struct, tagName){ + this.$init(tagName || "series", apf.NODE_HIDDEN, struct); +}; + +(function(){ + //1 = force no bind rule, 2 = force bind rule + this.$attrExcludePropBind = apf.extend({ + formula : 1 + }, this.$attrExcludePropBind); + + this.$propHandlers["formula"] = function(value, prop){ + delete this["c" + prop]; + } + + /*this.addEventListener("DOMNodeInsertedIntoDocument", function(e){ + //Find parent that this rule works on + var pNode = this; + while (pNode && pNode.$bindingRule) + pNode = pNode.parentNode; + + if (!pNode) + return; + });*/ +}).call(apf.BindingSeriesRule.prototype = new apf.BindingRule()); + +apf.aml.setElement("series", apf.BindingSeriesRule); + + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/elements/body.js)SIZE(1861)TIME(Fri, 14 Oct 2011 08:01:44 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + + +/** + * @todo description + * + * @author Ruben Daniels (ruben AT ajax DOT org) + * @version %I%, %G% + * @since 0.4 + */ + + + +apf.AmlConfig = function(){ + this.$init("config", apf.NODE_VISIBLE); +}; + +(function(){ + this.focussable = false; + this.$canLeechSkin = true; + + this.$draw = function(){ + //Build Main Skin + this.$ext = this.$int = this.$getExternal(this.$isLeechingSkin + ? this.localName + : "main"); + }; + +}).call(apf.AmlConfig.prototype = new apf.Presentation()); + +apf.aml.setElement("config", apf.AmlConfig); + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/elements/browser.js)SIZE(6466)TIME(Tue, 06 Dec 2011 16:22:54 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + + +/** + * Element displaying the rendered contents of an URL. + * + * @constructor + * @addnode elements:browser + * @define browser + * + * @inherits apf.XForms + * @inherits apf.StandardBinding + * @inherits apf.DataAction + * + * @event load + * @event error + * + * @author Ruben Daniels (ruben AT ajax DOT org) + * @version %I%, %G% + * @since 0.4 + * + * @binding value Determines the way the value for the element is retrieved + * from the bound data. + * Example (BROKEN): + * Sets the url based on data loaded into this component. + * + * + * + * + * + * + * Example: + * A shorter way to write this is: + * + * + * + * + * + * + */ +apf.browser = function(struct, tagName){ + this.$init(tagName || "browser", apf.NODE_VISIBLE, struct); +}; +(function(){ + this.implement( + + + apf.DataAction + + + ,apf.StandardBinding + + ); + + /** + * @attribute {String} src the url to be displayed in this element + * @attribute {String} value alias for the 'url' attribute + */ + this.$supportedProperties.push("value", "src"); + this.$propHandlers["src"] = + this.$propHandlers["value"] = function(value, force){ + try { + this.$browser.src = value || "about:blank"; + } + catch(e) { + this.$browser.src = "about:blank"; + } + }; + this.$propHandlers["border"] = apf.Presentation.prototype.$propHandlers["border"]; + + this.getValue = function() { + return this.value || this.src; + }; + + /** + * Retrieves the current url that is displayed. + */ + this.getURL = function(){ + return this.$browser.src; + }; + + /** + * Browses to the previous page + */ + this.back = function(){ + this.$browser.contentWindow.history.back(); + }; + + /** + * Browses to the next page + */ + this.forward = function(){ + this.$browser.contentWindow.history.forward(); + }; + + /** + * Reload the current page + */ + this.reload = function(){ + this.$browser.src = this.$browser.src; + }; + + /** + * Print the currently displayed page + */ + this.print = function(){ + this.$browser.contentWindow.print(); + }; + + /** + * Execute a string of javascript on the page. This is subject to browser + * security and will most likely only work when the browsed page is loaded + * from the same domain. + * @param {String} str javascript string to be executed. + * @param {Boolean} noError whether the execution can throw an exception. Defaults to false. + */ + this.runCode = function(str, noError){ + if (noError) { + try { + this.$browser.contentWindow.eval(str); + } catch(e) {} + } + else { + this.$browser.contentWindow.eval(str); + } + }; + + this.$draw = function(parentNode){ + if (!parentNode) + parentNode = this.$pHtmlNode; + + //Build Main Skin + if (apf.cannotSizeIframe) { + //parentNode.appendChild(document.createElement("iframe"));// + var _iframe = document.createElement("iframe"); + _iframe.setAttribute("frameborder","0"); + this.$ext = parentNode.appendChild(document.createElement("DIV")) + .appendChild(_iframe).parentNode; + this.$ext.style.width = "100px"; + this.$ext.style.height = "100px"; + this.$browser = this.$ext.firstChild; + //this.$browser = this.$ext; + this.$browser.style.width = "100%"; + this.$browser.style.height = "100%"; + this.$browser.frameBorder = 0; + } + else { + this.$ext = parentNode.appendChild(document.createElement("iframe")); + this.$ext.setAttribute("frameborder","0"); + //this.$ext.style.width = "100px"; + //this.$ext.style.height = "100px"; + this.$browser = this.$ext; + this.$ext.style.border = "1px solid #999"; + this.$ext.style.background = "white"; + } + this.$ext.className = "apfbrowser" + + if (this.getAttribute("style")) + this.$ext.setAttribute("style", this.getAttribute("style")); + + var _self = this; + apf.addListener(this.$browser, "load", function(){ + var loc = this.contentWindow.location.href; + _self.dispatchEvent("load", {href: loc}); + if (loc) + _self.setProperty("src", loc); + }); + + apf.addListener(this.$browser, "error", function(){ + _self.dispatchEvent("error"); + if (this.contentWindow.location.href) + _self.setProperty("src", this.contentWindow.location.href); + }); + + //this.$browser = this.$ext.contentWindow.document.body; + this.$ext.host = this; + //this.$browser.host = this; + }; +}).call(apf.browser.prototype = new apf.GuiElement()); + +apf.aml.setElement("browser", apf.browser); + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/elements/button.js)SIZE(31035)TIME(Tue, 13 Dec 2011 13:33:58 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + + +/** + * Element displaying a clickable rectangle that visually confirms to the + * user when the area is clicked and then executes a command. + * + * @constructor + * @define button, submit, trigger, reset + * @addnode elements + * + * @author Ruben Daniels (ruben AT ajax DOT org) + * @version %I%, %G% + * @since 0.4 + * + * @inherits apf.BaseButton + */ +apf.submit = function(struct, tagName){ + this.$init(tagName || "submit", apf.NODE_VISIBLE, struct); +}; + +apf.trigger = function(struct, tagName){ + this.$init(tagName || "trigger", apf.NODE_VISIBLE, struct); +}; + +apf.reset = function(struct, tagName){ + this.$init(tagName || "reset", apf.NODE_VISIBLE, struct); +}; + +apf.button = function(struct, tagName){ + this.$init(tagName || "button", apf.NODE_VISIBLE, struct); +}; + +(function() { + this.$useExtraDiv; + this.$childProperty = "caption"; + this.$inited = false; + this.$isLeechingSkin = false; + this.$canLeechSkin = true; + + /**** Properties and Attributes ****/ + + this.$focussable = apf.KEYBOARD; // This object can get the focus + this.value = null; + + this.$init(function(){ + //@todo reparenting + var forceFocus, _self = this, lastDefaultParent; + this.$propHandlers["default"] = function(value){ + if (parseInt(value) != value) + value = apf.isTrue(value) ? 1 : 0; + + this["default"] = parseInt(value); + + if (!this.focussable && value || forceFocus) + this.setAttribute("focussable", forceFocus = value); + + if (lastDefaultParent) { + lastDefaultParent.removeEventListener("focus", setDefault); + lastDefaultParent.removeEventListener("blur", removeDefault); + } + + if (!value) + return; + + var pNode = this.parentNode; + while (pNode && !pNode.focussable && value--) + pNode = pNode.parentNode; + + //Currrently only support for parentNode, this might need to be expanded + if (pNode) { + pNode.addEventListener("focus", setDefault); + pNode.addEventListener("blur", removeDefault); + } + }; + + function setDefault(e){ + if (e.defaultButtonSet || e.returnValue === false) + return; + + e.defaultButtonSet = true; + + if (this.$useExtraDiv) + _self.$ext.appendChild(apf.button.$extradiv); + + _self.$setStyleClass(_self.$ext, _self.$baseCSSname + "Default"); + + if (e.srcElement != _self && _self.$focusParent) { + _self.$focusParent.addEventListener("keydown", btnKeyDown); + } + } + + function removeDefault(e){ + if (this.$useExtraDiv && apf.button.$extradiv.parentNode == _self.$ext) + _self.$ext.removeChild(apf.button.$extradiv); + + _self.$setStyleClass(_self.$ext, "", [_self.$baseCSSname + "Default"]); + + if (e.srcElement != _self && _self.$focusParent) { + _self.$focusParent.removeEventListener("keydown", btnKeyDown); + } + } + + function btnKeyDown(e){ + var ml; + + var f = apf.document.activeElement; + if (f) { + if (f.hasFeature(apf.__MULTISELECT__)) + return; + + ml = f.multiline; + } + + if (!_self.$ext.onmouseup) + return; + + if (ml && ml != "optional" && e.keyCode == 13 + && e.ctrlKey || (!ml || ml == "optional") + && e.keyCode == 13 && !e.ctrlKey && !e.shiftKey && !e.altKey) { + apf.preventDefault(e.htmlEvent); + _self.$ext.onmouseup(e.htmlEvent, true); + } + } + + this.addEventListener("focus", setDefault); + this.addEventListener("blur", removeDefault); + + this.$enable = function(){ + if (this["default"]) { + setDefault({}); + if (apf.document.activeElement) + apf.document.activeElement.focus(true); + } + + if (this.state && this.value) + this.$setState("Down", {}); + else if (this.$mouseOver) + this.$updateState({}, "mouseover"); + else + this.$doBgSwitch(1); + }; + + this.$disable = function(){ + if (this["default"]) + removeDefault({}); + + this.$doBgSwitch(4); + this.$setStyleClass(this.$ext, "", + [this.$baseCSSname + "Over", this.$baseCSSname + "Down"]); + }; + }); + + /** + * @attribute {String} icon the url from which the icon image is loaded. + * @attribute {Boolean} state whether this boolean is a multi state button. + * @attribute {String} value the initial value of a state button. + * @attribute {String} color the text color of the caption of this element. + * @attribute {String} caption the text displayed on this element indicating the action when the button is pressed. + * @attribute {String} action one of the default actions this button can perform when pressed. + * Possible values: + * undo Executes undo on the action tracker of the target element. + * redo Executes redo on the action tracker of the target element. + * remove Removes the selected node(s) of the target element. + * add Adds a node to the target element. + * rename Starts the rename function on the target element. + * login Calls log in on the auth element with the values of the textboxes of type username and password. + * logout Calls lot out on the auth element. + * submit Submits the data of a model specified as the target. + * ok Executes a commitTransaction() on the target element, and closes or hides that element. + * cancel Executes a rollbackTransaction() on the target element, and closes or hides that element. + * apply Executes a commitTransaction() on the target element. + * close Closes the target element. + * @attribute {String} target id of the element to apply the action to. Defaults to the parent container. + * @attribute {Number} default Search depth for which this button is the default action. 1 specifies the direct parent. 2 the parent of this parent. Et cetera. + * @attribute {String} submenu the name of the contextmenu to display when the button is pressed. + */ + //this.$booleanProperties["default"] = true; + this.$booleanProperties["state"] = true; + this.$supportedProperties.push("icon", "value", "tooltip", "state", + "color", "caption", "action", "target", "default", "submenu", "hotkey"); + + this.$propHandlers["icon"] = function(value){ + + if (!this.oIcon) return; + + + if (value) + this.$setStyleClass(this.$ext, this.$baseCSSname + "Icon"); + else + this.$setStyleClass(this.$ext, "", [this.$baseCSSname + "Icon"]); + + apf.skins.setIcon(this.oIcon, value, this.iconPath); + }; + + this.$propHandlers["value"] = function(value){ + if (!this.state && !this.submenu) + return; + + if (value === undefined) + value = !this.value; + this.value = value; + + if (this.value) + this.$setState("Down", {}); + else + this.$setState("Out", {}); + }; + + this.$propHandlers["state"] = function(value){ + if (value) + this.$setStateBehaviour(this.value); + else + this.$setNormalBehaviour(); + }; + + this.$propHandlers["color"] = function(value){ + if (this.oCaption) + this.oCaption.parentNode.style.color = value; + }; + + this.$propHandlers["caption"] = function(value){ + if (!this.oCaption) + return; + + if (value) + this.$setStyleClass(this.$ext, "", [this.$baseCSSname + "Empty"]); + else + this.$setStyleClass(this.$ext, this.$baseCSSname + "Empty"); + + if (this.oCaption.nodeType == 1) + this.oCaption.innerHTML = String(value || "").trim(); + else + this.oCaption.nodeValue = String(value || "").trim(); + }; + + + /** + * @attribute {String} hotkey the key combination a user can press + * to active the function of this element. Use any combination of + * Ctrl, Shift, Alt, F1-F12 and alphanumerical characters. Use a + * space, a minus or plus sign as a seperator. + * Example: + * + * Undo + * + */ + this.$propHandlers["hotkey"] = function(value){ + if (this.$hotkey) + apf.setNodeValue(this.$hotkey, value); + + if (this.$lastHotkey) { + apf.hotkeys.remove(this.$lastHotkey[0], this.$lastHotkey[1]); + delete this.$lastHotkey[0]; + } + + if (value) { + this.$lastHotkey = [value]; + var _self = this; + apf.hotkeys.register(value, this.$lastHotkey[1] = function(){ + //hmm not very scalable... + _self.$setState("Over", {}); + + $setTimeout(function(){ + _self.$setState("Out", {}); + }, 200); + + if (_self.$clickHandler && _self.$clickHandler()) + _self.$updateState(e || event, "click"); + else + _self.dispatchEvent("click"); + }); + } + + if (this.tooltip) + apf.GuiElement.propHandlers.tooltip.call(this, this.tooltip); + } + + + + + //@todo move this to menu.js + function menuKeyHandler(e){ + return; + var key = e.keyCode; + + var next, nr = apf.getChildNumber(this); + if (key == 37) { //left + next = nr == 0 + ? this.parentNode.childNodes.length - 1 + : nr - 1; + this.parentNode.childNodes[next].dispatchEvent("mouseover"); + } + else if (key == 39) { //right + next = (nr >= this.parentNode.childNodes.length - 1) + ? 0 + : nr + 1; + this.parentNode.childNodes[next].dispatchEvent("mouseover"); + } + } + + function menuDown(e){ + var menu = self[this.submenu], + $button1; + + this.value = !this.value; + + if (this.value) + this.$setState("Down", {}); + + + + var menuPressed = this.parentNode.menuIsPressed; + if (menuPressed && menuPressed != this) { + menuPressed.setValue(false); + var oldMenu = self[menuPressed.submenu]; + if (oldMenu != self[this.submenu]) + oldMenu.$propHandlers["visible"].call(oldMenu, false, true); + } + + if (!this.value) { + menu.hide(); + this.$setState("Over", {}, "toolbarover"); + + if($button1 = this.parentNode.$button1) + $button1.$setState("Over", {}, "toolbarover"); + + this.parentNode.menuIsPressed = false; + if (this.parentNode.hasMoved) + this.value = false; + + if (apf.hasFocusBug) + apf.window.$focusfix(); + + return false; + } + + this.parentNode.menuIsPressed = this; + + //var pos = apf.getAbsolutePosition(this.$ext, menu.$ext.offsetParent); + menu.display(null, null, false, this, + null, null, this.$ext.offsetWidth - 2); + + this.parentNode.hasMoved = false; + + if (e) + apf.stopPropagation(e.htmlEvent); + + return false; + } + + function menuOver(){ + var menuPressed = this.parentNode.menuIsPressed; + + if (!menuPressed || menuPressed == this) + return; + + var menu = self[this.submenu]; + if (menu.pinned) + return; + + menuPressed.setValue(false); + var oldMenu = self[menuPressed.submenu]; + oldMenu.$propHandlers["visible"].call(oldMenu, false, true);//.hide(); + + this.setValue(true); + this.parentNode.menuIsPressed = this; + + + + var pos = apf.getAbsolutePosition(this.$ext, menu.$ext.offsetParent); + + menu.display(pos[0], + pos[1] + this.$ext.offsetHeight, true, this, + null, null, this.$ext.offsetWidth - 2); + + //apf.window.$focus(this); + this.$focus(); + + this.parentNode.hasMoved = true; + + return false; + } + + /** + * @attribute {string} submenu If this attribute is set, the button will + * function like a menu button + */ + this.$propHandlers["submenu"] = function(value){ + if (!value){ + if (this.value && this.parentNode) { + + menuDown.call(this); + + } + + this.$focussable = true; + this.$setNormalBehaviour(); + this.removeEventListener("mousedown", menuDown); + this.removeEventListener("mouseover", menuOver); + this.removeEventListener("keydown", menuKeyHandler, true); + return; + } + + this.$focussable = false; + this.$setStateBehaviour(); + + this.addEventListener("mouseover", menuOver); + this.addEventListener("mousedown", menuDown); + this.addEventListener("keydown", menuKeyHandler, true); + }; + + + /**** Public Methods ****/ + + + + /** + * Sets the value of this element. This should be one of the values + * specified in the values attribute. + * @param {String} value the new value of this element + */ + this.setValue = function(value){ + this.setProperty("value", value, false, true); + }; + + this.showMenu = function(){ + if (this.submenu && !this.value) + menuDown.call(this); + } + + this.hideMenu = function(){ + if (this.submenu && this.value) + menuDown.call(this); + } + + /** + * Sets the text displayed as caption of this element. + * + * @param {String} value required The string to display. + * @see baseclass.validation + */ + this.setCaption = function(value){ + this.setProperty("caption", value, false, true); + }; + + /** + * Sets the URL of the icon displayed on this element. + * + * @param {String} value required The URL to the location of the icon. + * @see element.button + * @see element.modalwindow + */ + this.setIcon = function(url){ + this.setProperty("icon", url, false, true); + }; + + + + /**** Private state methods ****/ + + this.$setStateBehaviour = function(value){ + this.value = value || false; + this.isBoolean = true; + this.$setStyleClass(this.$ext, this.$baseCSSname + "Bool"); + + if (this.value) { + this.$setStyleClass(this.$ext, this.$baseCSSname + "Down"); + this.$doBgSwitch(this.states["Down"]); + } + }; + + this.$setNormalBehaviour = function(){ + this.value = null; + this.isBoolean = false; + this.$setStyleClass(this.$ext, "", [this.$baseCSSname + "Bool"]); + }; + + this.$setState = function(state, e, strEvent){ + var parentNode = this.parentNode; + //if (this.disabled) + //return; + + if (strEvent && this.dispatchEvent(strEvent, {htmlEvent: e}) === false) + return; + + if (parentNode && parentNode.$button2 && parentNode.$button2.value && !this.submenu) + return; + + this.$doBgSwitch(this.states[state]); + var bs = this.$baseCSSname; + this.$setStyleClass(this.$ext, (state != "Out" ? bs + state : ""), + [(this.value ? "" : bs + "Down"), bs + "Over"]); + + if (this.submenu) { + bs = this.$baseCSSname + "menu"; + this.$setStyleClass(this.$ext, (state != "Out" ? bs + state : ""), + [(this.value ? "" : bs + "Down"), bs + "Over"]); + } + + //if (state != "Down") + //e.cancelBubble = true; + }; + + this.$clickHandler = function(){ + // This handles the actual OnClick action. Return true to redraw the button. + if (this.isBoolean && !this.submenu) { + this.setProperty("value", !this.value); + return true; + } + }; + + + this.$submenu = function(hide, force){ + if (hide) { + this.setValue(false); + this.$setState("Out", {}, "mouseout"); + if(this.parentNode) + this.parentNode.menuIsPressed = false; + } + }; + + + /**** Init ****/ + + this.addEventListener("$skinchange", function(e){ + if (this.tooltip) + apf.GuiElement.propHandlers.tooltip.call(this, this.tooltip); + }); + + this.$draw = function(){ + var pNode, isToolbarButton = (pNode = this.parentNode) + && pNode.parentNode && pNode.parentNode.localName == "toolbar"; + + if (isToolbarButton) { + if (typeof this.focussable == "undefined") + this.focussable = false; + + this.$focussable = apf.KEYBOARD; + } + + //Build Main Skin + this.$ext = this.$getExternal(); + this.oIcon = this.$getLayoutNode("main", "icon", this.$ext); + this.oCaption = this.$getLayoutNode("main", "caption", this.$ext); + + this.$useExtraDiv = apf.isTrue(this.$getOption("main", "extradiv")); + if (!apf.button.$extradiv && this.$useExtraDiv) { + (apf.button.$extradiv = document.createElement("div")) + .className = "extradiv" + } + + if (this.localName == "submit") + this.action = "submit"; + else if (this.localName == "reset") + this.action = "reset"; + + this.$setupEvents(); + }; + + + this.addEventListener("$skinchange", function(){ + if (this.caption) + this.$propHandlers["caption"].call(this, this.caption); + + if (this.icon) + this.$propHandlers["icon"].call(this, this.icon); + + this.$updateState({reset:1}); + //this.$blur(); + + //if (this.$focussable !== true && this.hasFocus()) + //apf.window.$focusLast(this.$focusParent); + }); + + + + //@todo solve how this works with XForms + this.addEventListener("click", function(e){ + var action = this.action; + + //#-ifdef __WITH_HTML5 + if (!action) + action = this.localName; + //#-endif + + var _self = this; + $setTimeout(function(){ + (apf.button.actions[action] || apf.K).call(_self); + }); + }); + + + +}).call(apf.button.prototype = new apf.BaseButton()); + +// submit, trigger, reset, button +apf.submit.prototype = +apf.trigger.prototype = +apf.reset.prototype = apf.button.prototype; + +apf.aml.setElement("submit", apf.submit); +apf.aml.setElement("trigger", apf.trigger); +apf.aml.setElement("reset", apf.reset); +apf.aml.setElement("button", apf.button); + + +apf.submit.action = +apf.trigger.actions = +apf.reset.actions = +apf.button.actions = { + + "undo" : function(action){ + var tracker; + if (this.target && self[this.target]) { + tracker = self[this.target].localName == "actiontracker" + ? self[this.target] + : self[this.target].getActionTracker(); + } + else { + var at, node = this; + while(node.parentNode) + at = (node = node.parentNode).$at; + } + + (tracker || apf.window.$at)[action || "undo"](); + }, + + "redo" : function(){ + apf.button.actions.undo.call(this, "redo"); + }, + + + + "remove" : function(){ + if (this.target && self[this.target]) + self[this.target].remove() + + }, + + "add" : function(){ + if (this.target && self[this.target]) + self[this.target].add() + + }, + + "rename" : function(){ + if (this.target && self[this.target]) + self[this.target].startRename() + + }, + + + + "login" : function(){ + var parent = this.target && self[this.target] + ? self[this.target] + : this.parentNode; + + var vg = parent.$validgroup || new apf.ValidationGroup(); + if (!vg.childNodes.length) + vg.childNodes = parent.childNodes.slice(); + + var vars = {}; + function loopChildren(nodes){ + for (var node, i = 0, l = nodes.length; i < l; i++) { + node = nodes[i]; + + if (node.hasFeature(apf.__VALIDATION__) + && !node.$validgroup && !node.form) { + node.setProperty("validgroup", vg); + } + + if (node.type) + vars[node.type] = node.getValue(); + + if (vars.username && vars.password) + return; + + if (node.childNodes.length) + loopChildren(node.childNodes); + } + } + loopChildren(parent.childNodes); + + if (!vg.isValid()) + return; + + if (!vars.username || !vars.password) { + + + return; + } + + var auth = this.ownerDocument.getElementsByTagNameNS(apf.ns.apf,"auth")[0]; + if (!auth) + return; + + auth.logIn(vars.username, vars.password); + //apf.auth.login(vars.username, vars.password); + }, + + "logout" : function(){ + var auth = this.ownerDocument.getElementsByTagNameNS(apf.ns.apf, "auth")[0]; + if (!auth) + return; + + auth.logOut(); + }, + + + + "submit" : function(doReset){ + var vg, model; + + var parent = this.target && self[this.target] + ? self[this.target] + : this.parentNode; + + if (parent.$isModel) + model = parent; + else { + if (!parent.$validgroup) { + parent.$validgroup = parent.validgroup + ? self[parent.validgroup] + : new apf.ValidationGroup(); + } + + vg = parent.$validgroup; + if (!vg.childNodes.length) + vg.childNodes = parent.childNodes.slice(); + + function loopChildren(nodes){ + for (var node, i = 0, l = nodes.length; i < l; i++) { + node = nodes[i]; + + if (node.getModel) { + model = node.getModel(); + if (model) + return false; + } + + if (node.childNodes.length) + if (loopChildren(node.childNodes) === false) + return false; + } + } + loopChildren(parent.childNodes); + + if (!model) { + model = apf.globalModel; + if (!model) { + + + return; + } + } + } + + if (doReset) { + model.reset(); + return; + } + + if (vg && !vg.isValid()) + return; + + model.submit(); + }, + + "reset" : function(){ + apf.button.actions["submit"].call(this, true); + }, + + + + "ok" : function(){ + var node; + + if (this.target) { + node = self[this.target]; + } + else { + var node = this.parentNode; + while (node && !node.hasFeature(apf.__TRANSACTION__)) { + node = node.parentNode; + } + + if (node && !node.hasFeature(apf.__TRANSACTION__)) + return; + } + + if (node.commit() && node.close) + node.close(); + }, + + "cancel" : function(){ + var node; + + if (this.target) { + node = self[this.target]; + } + else { + var node = this.parentNode; + while (node && !node.hasFeature(apf.__TRANSACTION__)) { + node = node.parentNode; + } + + if (node && !node.hasFeature(apf.__TRANSACTION__)) + return; + } + + node.rollback(); + if (node.close) + node.close(); + }, + + "apply" : function(){ + var node; + + if (this.target) { + node = self[this.target]; + } + else { + var node = this.parentNode; + while (node && !node.hasFeature(apf.__TRANSACTION__)) { + node = node.parentNode; + } + + if (node && !node.hasFeature(apf.__TRANSACTION__)) + return; + } + + if (node.autoshow) + node.autoshow = -1; + if (node.commit(true)) + node.begin("update"); + }, + + + "close" : function(){ + var parent = this.target && self[this.target] + ? self[this.target] + : this.parentNode; + + while(parent && !parent.close) + parent = parent.parentNode; + + if (parent && parent.close) + parent.close(); + + } +}; + + + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/elements/caldropdown.js)SIZE(36424)TIME(Fri, 14 Oct 2011 08:01:44 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/elements/calendar.js)SIZE(28862)TIME(Tue, 06 Dec 2011 16:22:54 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/elements/calendarlist.js)SIZE(15123)TIME(Fri, 14 Oct 2011 08:01:44 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/elements/chart.js)SIZE(9687)TIME(Fri, 14 Oct 2011 08:01:44 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/elements/checkbox.js)SIZE(8188)TIME(Tue, 06 Dec 2011 16:22:54 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + + +/** + * Element displaying a clickable rectangle having two states which + * can be toggled by user interaction. + * Example: + * + * the glass is full + * + * + * @constructor + * + * @define checkbox + * @addnode elements + * + * @author Ruben Daniels (ruben AT ajax DOT org) + * @version %I%, %G% + * @since 0.4 + * + * @inherits apf.BaseButton + * @inherits apf.XForms + * + * @binding value Determines the way the value for the element is retrieved + * from the bound data. + * Example: + * Sets the value of the checkbox based on data loaded into this component. + * + * + * + * + * Caption + * + * Example: + * A shorter way to write this is: + * + * + * + * + * Caption + * + */ +apf.checkbox = function(struct, tagName){ + this.$init(tagName || "checkbox", apf.NODE_VISIBLE, struct); +}; + +(function() { + this.implement( + + + apf.DataAction + + ); + + //Options + this.$focussable = apf.KEYBOARD; // This object can get the focus + this.checked = false; + + /**** Properties and Attributes ****/ + + this.$booleanProperties["checked"] = true; + this.$supportedProperties.push("value", "checked", "label", "values"); + + /** + * @attribute {String} value the value of this element. + */ + this.$propHandlers["value"] = function(value){ + value = (typeof value == "string" ? value.trim() : value); + + if (value == "" && this["default"]) + value = this.value = apf.isTrue(this["default"]); + + if (this.$values) { + this.checked = (typeof value != "undefined" && value !== null + && value.toString() == this.$values[0].toString()); + } + else { + this.checked = apf.isTrue(value); + } + + if (this.checked) + apf.setStyleClass(this.$ext, this.$baseCSSname + "Checked"); + else + apf.setStyleClass(this.$ext, "", [this.$baseCSSname + "Checked"]); + }; + + /** + * @attribute {Boolean} checked whether the element is in the checked state. + */ + this.$propHandlers["checked"] = function(value) { + if (!this.$values) { + if (this.getAttribute("values")) + this.$propHandler["values"].call(this, this.getAttribute("values")); + //else + //this.$values = [true, false]; + } + this.setProperty("value", this.$values ? this.$values[value ? 0 : 1] : true); + }; + + /** + * @attribute {String} label the caption of the label explaining what + * the meaning of the checked state of this element is. + */ + this.$propHandlers["label"] = function(value){ + if (!this.$ext) + return; + + var lbl = this.$getLayoutNode("main", "label", this.$ext); + if (!lbl) + return; + + if (lbl.nodeType == 1) + lbl.innerHTML = value; + else + lbl.nodeValue = value; + }; + + /** + * @attribute {String} values a pipe seperated list of two values which + * correspond to the two states of the checkbox. The first for the checked + * state, the second for the unchecked state. Defaults to "true|false". + */ + this.$propHandlers["values"] = function(value){ + this.$values = typeof value == "string" + ? value.split("\|") + : (value || [1, 0]); + + this.$propHandlers["value"].call(this, this.value); + }; + + /**** Public Methods ****/ + + + + /** + * Sets the value of this element. This should be one of the values + * specified in the values attribute. + * @param {String} value the new value of this element + */ + this.setValue = function(value){ + if (!this.$values) return; + this.setProperty("value", value, false, true); + }; + + /** + * Returns the current value + */ + this.getValue = function(){ + return this.xmlRoot ? (this.$values + ? this.$values[this.checked ? 0 : 1] + : this.checked) : this.value; + }; + + /** + * Sets the checked state and related value + */ + this.check = function(){ + this.setProperty("value", this.$values + ? this.$values[0] + : true, false, true); + }; + + /** + * Sets the unchecked state and related value + */ + this.uncheck = function(){ + this.setProperty("value", this.$values + ? this.$values[1] + : false, false, true); + }; + + + + /**** Private state handling methods ****/ + + this.addEventListener("$clear", function(){ + this.setProperty("value", this.$values ? this.$values[1] : false); + }); + + this.$enable = function(){ + if (this.$input) this.$input.disabled = false; + this.$doBgSwitch(1); + }; + + this.$disable = function(){ + if (this.$input) this.$input.disabled = true; + this.$doBgSwitch(4); + }; + + this.$setState = function(state, e, strEvent){ + //if (this.disabled) return; + + this.$doBgSwitch(this.states[state]); + this.$setStyleClass(this.$ext, (state != "Out" ? this.$baseCSSname + state : ""), + [this.$baseCSSname + "Down", this.$baseCSSname + "Over"]); + this.state = state; // Store the current state so we can check on it coming here again. + + if (strEvent) + this.dispatchEvent(strEvent, {htmlEvent: e}); + + /*if (state == "Down") + apf.cancelBubble(e, this); + else + e.cancelBubble = true;*/ + }; + + this.$clickHandler = function(){ + //this.checked = !this.checked; + this.change(this.$values + ? this.$values[(!this.checked) ? 0 : 1] + : !this.checked); + + + if (this.validate) //@todo rewrite button + this.validate(true); + + + return true; + }; + + /**** Init ****/ + + this.$draw = function(){ + //Build Main Skin + this.$ext = this.$getExternal(); + this.$input = this.$getLayoutNode("main", "input", this.$ext); + this.$notfromext = this.$input && this.$input != this.$ext; + + this.$setupEvents(); + }; + + this.$childProperty = "label"; + + + this.addEventListener("$skinchange", function(){ + if (this.label) + this.$propHandlers["label"].call(this, this.label); + }) + + + + this.$getActiveElements = function() { + // init $activeElements + if (!this.$activeElements) { + this.$activeElements = { + $input : this.$input + } + } + + return this.$activeElements; + } + +}).call(apf.checkbox.prototype = new apf.BaseButton()); + +apf.aml.setElement("checkbox", apf.checkbox); + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/elements/collection.js)SIZE(2383)TIME(Fri, 14 Oct 2011 08:01:44 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/elements/colorpicker.js)SIZE(12736)TIME(Fri, 14 Oct 2011 08:01:44 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + + +/** + * Element giving the user a visual choice of several colors presented in a + * grid. + * + * @constructor + * @define colorpicker + * @addnode elements + * + * @author Ruben Daniels (ruben AT ajax DOT org) + * @version %I%, %G% + * @since 0.4 + * + * @inherits apf.StandardBinding + * @inherits apf.DataAction + * @inherits apf.XForms + * + * @attribute {String} color the color that is selected in the color picker. + * + * @binding value Determines the way the value for the element is retrieved + * from the bound data. + * Example: + * Sets the color based on data loaded into this component. + * + * + * + * + * + * + * Example: + * A shorter way to write this is: + * + * + * + * + * + * + */ +apf.colorpicker = function(struct, tagName){ + this.$init(tagName || "colorpicker", apf.NODE_VISIBLE, struct); +}; + +(function(){ + this.implement( + + apf.StandardBinding + + + ,apf.DataAction + + + ); + //Options + this.$focussable = true; // This object can get the focus + + // PUBLIC METHODS + this.setValue = function(value, type){ + //this.value = value; + if (!type) type = "RGBHEX"; + var a; + switch (type) { + case "HSL": + this.fill(value[0], value[1], value[2]); + break; + case "RGB": + a = RGBtoHLS(value[0], value[1], value[2]); + this.fill(a[0], a[1], a[2]); + break; + case "RGBHEX": + var RGB = arguments[0].match(/(..)(..)(..)/); + a = RGBtoHLS(Math.hexToDec(RGB[0]), + Math.hexToDec(RGB[1]), Math.hexToDec(RGB[2])); + this.fill(a[0], a[1], a[2]); + break; + } + }; + + this.getValue = function(type){ + return HSLRangeToRGB(cH, cS, cL); + }; + + // PRIVATE METHODS + var cL = 120, + cS = 239, + cH = 0, + cHex = "#FF0000", + HSLRange = 240; + + function HSLRangeToRGB(H, S, L){ + return HSLtoRGB(H / (HSLRange - 1), S / HSLRange, + Math.min(L / HSLRange, 1)) + }; + + function RGBtoHLS(R,G,B){ + var RGBMAX = 255, + HLSMAX = HSLRange, + UNDEF = (HLSMAX*2/3), + + /* calculate lightness */ + cMax = Math.max(Math.max(R,G), B), + cMin = Math.min(Math.min(R,G), B); + L = (((cMax + cMin) * HLSMAX) + RGBMAX) / (2 * RGBMAX); + + if (cMax == cMin) { /* r=g=b --> achromatic case */ + S = 0; /* saturation */ + H = UNDEF; /* hue */ + } + /* chromatic case */ + else { + /* saturation */ + if (L <= (HLSMAX/2)) + S = (((cMax - cMin) * HLSMAX) + ((cMax + cMin) / 2)) / (cMax + cMin); + else + S = (((cMax - cMin) * HLSMAX) + ((2 * RGBMAX - cMax - cMin) / 2)) + / (2 * RGBMAX - cMax - cMin); + + /* hue */ + Rdelta = (((cMax - R) * (HLSMAX / 6)) + ((cMax - cMin) / 2)) / (cMax - cMin); + Gdelta = (((cMax - G) * (HLSMAX / 6)) + ((cMax - cMin) / 2)) / (cMax - cMin); + Bdelta = (((cMax - B) * (HLSMAX / 6)) + ((cMax - cMin) / 2)) / (cMax - cMin); + + if (R == cMax) + H = Bdelta - Gdelta; + else if (G == cMax) + H = (HLSMAX / 3) + Rdelta - Bdelta; + else + H = ((2 * HLSMAX) / 3) + Gdelta - Rdelta; + + if (H < 0) + H += HLSMAX; + if (H > HLSMAX) + H -= HLSMAX; + } + + return [H, S, L]; + } + + function hueToColorValue(hue){ + var V; + + if (hue < 0) + hue = hue + 1 + else if (hue > 1) + hue = hue - 1; + + if (6 * hue < 1) + V = M1 + (M2 - M1) * hue * 6 + else if (2 * hue < 1) + V = M2 + else if (3 * hue < 2) + V = M1 + (M2 - M1) * (2 / 3 - hue) * 6 + else + V = M1; + + return Math.max(Math.floor(255 * V), 0); + }; + + function HSLtoRGB(H, S, L){ + var R, G, B; + + if (S == 0) + G = B = R = Math.round (255 * L); + else { + M2 = (L <= 0.5) ? (L * (1 + S)) : (L + S - L * S); + + M1 = 2 * L - M2; + R = hueToColorValue(H + 1 / 3); + G = hueToColorValue(H); + B = hueToColorValue(H - 1 / 3); + } + + return Math.decToHex(R) + "" + Math.decToHex(G) + "" + Math.decToHex(B); + }; + + this.fill = function(H, S, L){ + var Hex = HSLRangeToRGB(H,S,L); + this.value = Hex; + + //RGB + var RGB = Hex.match(/(..)(..)(..)/); + this.tbRed.value = Math.hexToDec(RGB[1]); + this.tbGreen.value = Math.hexToDec(RGB[2]); + this.tbBlue.value = Math.hexToDec(RGB[3]); + + //HSL + this.tbHue.value = Math.round(H); + this.tbSatern.value = Math.round(S); + this.tbLuminance.value = Math.round(L); + + //HexRGB + this.tbHexColor.value = Hex; + + //Shower + this.shower.style.backgroundColor = Hex; + + //Luminance + var HSL120 = HSLRangeToRGB(H, S, 120); + this.bar1.style.backgroundColor = HSL120; + this.bgBar1.style.backgroundColor = HSLRangeToRGB(H, S, 240); + this.bar2.style.backgroundColor = HSLRangeToRGB(H, S, 0); + this.bgBar2.style.backgroundColor = HSL120; + }; + + this.movePointer = function(e){ + e = e || event; + + var ty = this.pHolder.ty; + if ((e.clientY - ty >= 0) && (e.clientY - ty + <= this.pHolder.offsetHeight - this.pointer.offsetHeight + 22)) + this.pointer.style.top = e.clientY - ty; + if (e.clientY - ty < 21) + this.pointer.style.top = 21; + if (e.clientY - ty + > this.pHolder.offsetHeight - this.pointer.offsetHeight + 19) + this.pointer.style.top = this.pHolder.offsetHeight + - this.pointer.offsetHeight + 19; + + // 255 - posY: + cL = (255 - (this.pointer.offsetTop - 22)) / 2.56 * 2.4; + this.fill(cH, cS, cL); + + e.returnValue = false; + e.cancelBubble = true; + }; + + this.setLogic = function(){ + var _self = this; + this.pHolder.style.zIndex = 10; + this.pHolder.onmousedown = function(){ + this.ty = apf.getAbsolutePosition(this)[1] - 20; + + _self.movePointer(); + document.onmousemove = _self.movePointer + document.onmouseup = function(){ this.onmousemove = function(){}; }; + } + + this.container.onmousedown = function(e){ + e = e || event; + + this.active = true; + if (e.srcElement == this) { + if (e.offsetX >= 0 && e.offsetX <= 256 + && e.offsetY >= 0 && e.offsetY <= 256) { + cS = (256 - e.offsetY) / 2.56 * 2.4 + cH = e.offsetX / 2.56 * 2.39 + } + _self.fill(cH, cS, cL); + _self.shower.style.backgroundColor = _self.currentColor; + } + _self.point.style.display = "none"; + + e.cancelBubble = true; + } + + this.container.onmouseup = function(e){ + e = e || event; + this.active = false; + _self.point.style.top = e.offsetY - _self.point.offsetHeight - 2; + _self.point.style.left = e.offsetX - _self.point.offsetWidth - 2; + _self.point.style.display = "block"; + + _self.change(_self.tbHexColor.value); + } + + this.container.onmousemove = function(e){ + e = e || event; + if (this.active) { + if (e.offsetX >= 0 && e.offsetX <= 256 + && e.offsetY >= 0 && e.offsetY <= 256) { + cS = (256 - e.offsetY) / 2.56 * 2.4 + cH = e.offsetX / 2.56 * 2.39 + } + _self.fill(cH, cS, cL); + _self.shower.style.backgroundColor = _self.currentColor; + } + } + + /*this.tbHexColor.host = + this.tbRed.host = + this.tbGreen.host = + this.tbBlue.host = this; + this.tbHexColor.onblur = function(){_self.setValue("RGBHEX", this.value);} + this.tbRed.onblur = function(){_self.setValue("RGB", this.value, _self.tbGreen.value, _self.tbBlue.value);} + this.tbGreen.onblur = function(){_self.setValue("RGB", _self.tbRed.value, this.value, _self.tbBlue.value);} + this.tbBlue.onblur = function(){_self.setValue("RGB", _self.tbRed.value, _self.tbGreen.value, this.value);} + */ + } + + // Databinding + this.$mainBind = "color"; + + this.$draw = function(parentNode, clear){ + //Build Main Skin + this.$ext = this.$getExternal(); + + this.tbRed = this.$getLayoutNode("main", "red", this.$ext); + this.tbGreen = this.$getLayoutNode("main", "green", this.$ext); + this.tbBlue = this.$getLayoutNode("main", "blue", this.$ext); + + this.tbHue = this.$getLayoutNode("main", "hue", this.$ext); + this.tbSatern = this.$getLayoutNode("main", "satern", this.$ext); + this.tbLuminance = this.$getLayoutNode("main", "luminance", this.$ext); + + this.tbHexColor = this.$getLayoutNode("main", "hex", this.$ext); + var _self = this; + this.tbHexColor.onchange = function(){ + _self.setValue(this.value, "RGBHEX"); + }; + + this.shower = this.$getLayoutNode("main", "shower", this.$ext); + + this.bar1 = this.$getLayoutNode("main", "bar1", this.$ext); + this.bgBar1 = this.$getLayoutNode("main", "bgbar1", this.$ext); + this.bar2 = this.$getLayoutNode("main", "bar2", this.$ext); + this.bgBar2 = this.$getLayoutNode("main", "bgbar2", this.$ext); + + this.pHolder = this.$getLayoutNode("main", "pholder", this.$ext); + this.pointer = this.$getLayoutNode("main", "pointer", this.$ext); + this.container = this.$getLayoutNode("main", "container", this.$ext); + this.point = this.$getLayoutNode("main", "point", this.$ext); + + var nodes = this.$ext.getElementsByTagName("input"); + for (var i = 0; i < nodes.length; i++) { + nodes[i].onselectstart = function(e){ + e = e || event; + e.cancelBubble = true; + }; + } + + this.setLogic(); + + this.setValue("ffffff"); + //this.fill(cH, cS, cL); + } + + this.$loadAml = function(x){ + if (x.getAttribute("color")) + this.setValue(x.getAttribute("color")); + } + + this.$destroy = function(){ + this.container.host = + this.tbRed.host = + this.tbGreen.host = + this.tbBlue.host = + this.tbHexColor.host = + this.pHolder.host = null; + } +}).call(apf.colorpicker.prototype = new apf.GuiElement()); + +apf.aml.setElement("colorpicker", apf.colorpicker); + + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/elements/colorpicker2.js)SIZE(14420)TIME(Wed, 30 Nov 2011 17:29:20 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + + +/** + * Element giving the user a visual choice to pick a color just like Photoshop + * does it! + * + * @constructor + * @define colorpicker + * @addnode elements + * + * @author Mike de Boer (mike AT javeline DOT com) + * @version %I%, %G% + * @since 3.0 + * + * @inherits apf.StandardBinding + * @inherits apf.DataAction + * + * @attribute {String} value the color that is selected in the color picker. + * + * @binding value Determines the way the value for the element is retrieved + * from the bound data. + * Example: + * Sets the color based on data loaded into this component. + * + * + * + * + * + * + */ +apf.colorpicker = function(struct, tagName){ + this.$init(tagName || "colorpicker", apf.NODE_VISIBLE, struct); +}; + +(function(){ + this.value = "ff0000"; + this.changeTimer = null; + + var c = apf.color; + + this.$supportedProperties.push("color", "red", "green", "blue", "hue", + "saturation", "brightness", "hex"); + + this.$propHandlers["red"] = + this.$propHandlers["green"] = + this.$propHandlers["blue"] = + this.$propHandlers["hue"] = + this.$propHandlers["saturation"] = + this.$propHandlers["brightness"] = + this.$propHandlers["hex"] = function(val, doChange) { + clearTimeout(this.changeTimer); + if (doChange) { + var _self = this; + this.changeTimer = $setTimeout(function() { + _self.$change(); + }); + } + }; + + this.$propHandlers["value"] = function(val) { + this.$restoreOriginal(); + }; + + this.$restoreOriginal = function() { + this.$change(c.hexToHSB(this.value)); + this.oCustomColor.style.backgroundColor = + (this.value.substr(0, 1) != "#" ? "#" : "") + this.value; + }; + + this.$change = function(hsb) { + if (!hsb) { + hsb = { + h: this.hue, + s: this.saturation, + b: this.brightness + }; + } + hsb = c.fixHSB(hsb); + + var hex = c.HSBToHex(hsb), + rgb = c.HSBToRGB(hsb); + + this.oNewColor.style.backgroundColor = "#" + hex; + + this.setProperty("red", rgb.r); + this.setProperty("green", rgb.g); + this.setProperty("blue", rgb.b); + this.setProperty("saturation", hsb.s); + this.setProperty("brightness", hsb.b); + this.setProperty("hue", hsb.h); + this.setProperty("hex", hex); + + this.oSelector.style.background = "#" + c.HSBToHex({h: hsb.h, s: 100, b: 100}); + this.oHue.style.top = parseInt(150 - 150 * hsb.h / 360, 10) + "px"; + this.oSelectorInd.style.left = parseInt(150 * hsb.s / 100, 10) + "px"; + this.oSelectorInd.style.top = parseInt(150 * (100 - hsb.b) / 100, 10) + "px"; + }; + + this.$draw = function() { + if (!this.id) + this.setProperty("id", "colorpicker" + this.$uniqueId); + + //Build Main Skin + this.$ext = this.$getExternal(); + this.oSelector = this.$getLayoutNode("main", "selector", this.$ext); + this.oSelectorInd = this.$getLayoutNode("main", "selector_indic", this.$ext); + this.oHue = this.$getLayoutNode("main", "hue", this.$ext); + this.oNewColor = this.$getLayoutNode("main", "newcolor", this.$ext); + this.oCustomColor = this.$getLayoutNode("main", "customcolor", this.$ext); + this.oInputs = this.$getLayoutNode("main", "inputs", this.$ext); + + this.$restoreOriginal(); + + //attach behaviours + var _self = this, + doc = (!document.compatMode || document.compatMode == 'CSS1Compat') + ? document.html : document.body; + function stopMoving() { + document.onmousemove = document.onmouseup = null; + _self.$change(); + return false; + } + function selectorDown() { + var el = this, + pos = apf.getAbsolutePosition(el); + + function selectorMove(e) { + e = e || event; + var pageX = e.pageX || e.clientX + (doc ? doc.scrollLeft : 0), + pageY = e.pageY || e.clientY + (doc ? doc.scrollTop : 0); + // only the saturation and brightness change... + _self.brightness = parseInt(100 * (150 - Math.max(0, Math.min(150, + (pageY - pos[1])))) / 150, 10); + _self.saturation = parseInt(100 * (Math.max(0, Math.min(150, + (pageX - pos[0])))) / 150, 10); + _self.$change(); + pos = apf.getAbsolutePosition(el); + return false; + } + document.onmousemove = selectorMove; + document.onmouseup = function(e) { + selectorMove(e); + return stopMoving(e); + }; + } + + function hueDown(e) { + var el = this, + pos = apf.getAbsolutePosition(el); + + function hueMove(e) { + e = e || event; + var pageY = e.pageY || e.clientY + (doc ? doc.scrollTop : 0); + _self.hue = parseInt(360 * (150 - Math.max(0, + Math.min(150, (pageY - pos[1])))) / 150, 10); + _self.$change(); + pos = apf.getAbsolutePosition(el); + } + document.onmousemove = hueMove; + document.onmouseup = function(e) { + hueMove(e); + return stopMoving(e); + }; + } + this.oSelector.onmousedown = selectorDown; + this.oHue.parentNode.onmousedown = hueDown; + this.oCustomColor.onmousedown = function() { + _self.$restoreOriginal(); + }; + + function spinnerChange(e) { + var o = e.currentTarget, + isRGB = false; + if (o.id.indexOf("hue") > -1) + _self.hue = e.value; + else if (o.id.indexOf("saturation") > -1) + _self.saturation = e.value; + else if (o.id.indexOf("brightness") > -1) + _self.brightness = e.value; + else if (o.id.indexOf("red") > -1) + _self.red = e.value, isRGB = true; + else if (o.id.indexOf("green") > -1) + _self.green = e.value, isRGB = true; + else if (o.id.indexOf("blue") > -1) + _self.blue = e.value, isRGB = true; + + if (isRGB) { + var hsb = c.RGBToHSB({r: _self.red, g: _self.green, b: _self.blue}); + _self.hue = hsb.h; + _self.saturation = hsb.s; + _self.brightness = hsb.b; + } + + _self.$change(); + } + + //append APF widgets for additional controls + var skin = apf.getInheritedAttribute(this.parentNode, "skinset"); + new apf.hbox({ + htmlNode: this.oInputs, + skinset: skin, + left: 212, + top: 52, + width: 150, + padding: 3, + childNodes: [ + new apf.vbox({ + padding: 3, + childNodes: [ + new apf.hbox({ + childNodes: [ + new apf.label({ + width: 14, + caption: "R:", + "for": this.id + "_red" + }), + new apf.spinner({ + id: this.id + "_red", + width: 45, + min: 0, + max: 255, + value: "{" + this.id + ".red}", + onafterchange: spinnerChange + }) + ] + }), + new apf.hbox({ + childNodes: [ + new apf.label({ + width: 14, + caption: "G:", + "for": this.id + "_green" + }), + new apf.spinner({ + id: this.id + "_green", + width: 45, + min: 0, + max: 255, + value: "{" + this.id + ".green}", + onafterchange: spinnerChange + }) + ] + }), + new apf.hbox({ + childNodes: [ + new apf.label({ + width: 14, + caption: "B:", + "for": this.id + "_blue" + }), + new apf.spinner({ + id: this.id + "_blue", + width: 45, + min: 0, + max: 255, + value: "{" + this.id + ".blue}", + onafterchange: spinnerChange + }) + ] + }) + ] + }), + new apf.vbox({ + padding: 3, + childNodes: [ + new apf.hbox({ + childNodes: [ + new apf.label({ + width: 14, + caption: "H:", + "for": this.id + "_hue" + }), + new apf.spinner({ + id: this.id + "_hue", + width: 45, + min: 0, + max: 360, + value: "{" + this.id + ".hue}", + onafterchange: spinnerChange + }) + ] + }), + new apf.hbox({ + childNodes: [ + new apf.label({ + width: 14, + caption: "S:", + "for": this.id + "_saturation" + }), + new apf.spinner({ + id: this.id + "_saturation", + width: 45, + min: 0, + max: 100, + value: "{" + this.id + ".saturation}", + onafterchange: spinnerChange + }) + ] + }), + new apf.hbox({ + childNodes: [ + new apf.label({ + width: 14, + caption: "B:", + "for": this.id + "_brightness" + }), + new apf.spinner({ + id: this.id + "_brightness", + width: 45, + min: 0, + max: 100, + value: "{" + this.id + ".brightness}", + onafterchange: spinnerChange + }) + ] + }) + ] + }) + ] + }); + + new apf.label({ + htmlNode: this.oInputs, + skinset: skin, + left: 212, + top: 142, + width: 14, + caption: "#", + "for": this.id + "_hex" + }); + + new apf.textbox({ + htmlNode: this.oInputs, + skinset: skin, + left: 222, + top: 140, + width: 75, + value: "{" + this.id + ".hex}", + onafterchange: function(e) { + _self.hex = c.fixHex(e.value); + var hsb = c.hexToHSB(_self.hex); + _self.hue = hsb.h; + _self.saturation = hsb.s; + _self.brightness = hsb.b; + _self.$change(); + } + }); + }; + + this.$destroy = function() { + this.$ext = this.oSelector = this.oSelectorInd = this.oHue = + this.oNewColor = this.oCustomColor = this.oInputs = null; + }; + +}).call(apf.colorpicker.prototype = new apf.StandardBinding()); + + +apf.aml.setElement("colorpicker", apf.colorpicker); + + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/elements/comment.js)SIZE(1324)TIME(Fri, 14 Oct 2011 08:01:44 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + + +/** + * all elements within the comment tag are ignored by the parser. + * + * @author Ruben Daniels (ruben AT ajax DOT org) + * @version %I%, %G% + * @since 0.4 + */ +apf.comment = function(){ + this.$init("comment", apf.NODE_HIDDEN); +}; + +apf.comment.prototype = new apf.AmlComment(); +apf.aml.setElement("comment", apf.comment); + + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/elements/contextmenu.js)SIZE(2557)TIME(Fri, 14 Oct 2011 08:01:44 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + + +/** + * element specifying which menu is shown when a + * contextmenu is requested by a user for a aml node. + * Example: + * This example shows a list that shows the mnuRoot menu when the user + * right clicks on the root {@link term.datanode data node}. Otherwise the mnuItem menu is + * shown. + * + * + * + * + * + * + * @attribute {String} menu the id of the menu element. + * @attribute {String} select the xpath executed on the selected element of the databound element which determines whether this contextmenu is shown. + * + * @author Ruben Daniels (ruben AT ajax DOT org) + * @version %I%, %G% + * @since 0.4 + */ +apf.contextmenu = function(){ + this.$init("contextmenu", apf.NODE_HIDDEN); +}; + +(function(){ + this.$amlNodes = []; + + //1 = force no bind rule, 2 = force bind rule + this.$attrExcludePropBind = apf.extend({ + "match" : 1 + }, this.$attrExcludePropBind); + + this.register = function(amlParent){ + if (!amlParent.contextmenus) + amlParent.contextmenus = []; + amlParent.contextmenus.push(this); + }; + + this.unregister = function(amlParent){ + amlParent.contextmenus.remove(this); + }; + + this.addEventListener("DOMNodeInsertedIntoDocument", function(e){ + this.register(this.parentNode); + }); +}).call(apf.contextmenu.prototype = new apf.AmlElement()); + +apf.aml.setElement("contextmenu", apf.contextmenu); + + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/elements/datagrid.js)SIZE(53921)TIME(Tue, 13 Dec 2011 13:33:58 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + + +/** + * Element providing a sortable, selectable grid containing scrollable + * information. Grid columns can be reordered and resized. + * Example: + * This example shows a datagrid width several columns mixing percentage and + * fixed size columns. + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * @constructor + * @define datagrid + * @addnode elements + * + * @author Ruben Daniels (ruben AT ajax DOT org) + * @version %I%, %G% + * @since 0.4 + * + * @inherits apf.BaseTree + * + * @binding invalidmsg Determines the error message that is shown when a cell is not valid. + * @binding description Determines the text that is displayed under the expanded row. + */ +apf.datagrid = function(struct, tagName){ + this.$init(tagName || "datagrid", apf.NODE_VISIBLE, struct); + + this.$headings = [], + this.$cssRules = []; //@todo Needs to be reset; + this.$lastOpened = {}; + + this.$editors = {}; + + + this.$dynCssClasses = []; + +}; + +(function(){ + var treeState = this.$treeState; + + + this.implement( + apf.DataAction + ); + + + /*this.$init(function() { + this.addEventListener("keydown", keyHandler, true); + });*/ + + this.bufferselect = false; + this.$useTable = false; + this.$focussable = true; + this.$isWindowContainer = -1; + + this.$widthdiff = 0; + this.$defaultwidth = 0; + this.$useiframe = 0; + this.$needsDepth = true; + this.$fixed = 0; + + + this.canrename = false; //@todo remove rename from basetree and move to tree.js + + + /** + * @attribute {Boolean} iframe whether this element is rendered inside an iframe. This is only supported for IE. Default is false for datagrid and true for spreadsheet and propedit. + */ + this.$booleanProperties["iframe"] = true; + + + + /** + * @attribute {String} mode Sets the way this element interacts with the user. + * Possible values: + * check the user can select a single item from this element. The selected item is indicated. + * radio the user can select multiple items from this element. Each selected item is indicated. + */ + this.$mode = 0; + this.$propHandlers["mode"] = function(value){ + if ("check|radio".indexOf(value) > -1) { + if (!this.hasFeature(apf.__MULTICHECK__)) + this.implement(apf.MultiCheck); + + this.addEventListener("afterrename", $afterRenameMode); //what does this do? + + this.multicheck = value == "check"; //radio is single + this.$mode = this.multicheck ? 1 : 2; + } + else { + //@todo undo actionRules setting + this.removeEventListener("afterrename", $afterRenameMode); + //@todo unimplement?? + this.$mode = 0; + } + }; + + //@todo apf3.0 retest this completely + function $afterRenameMode(){ + } + + + + this.$propHandlers["options"] = function(value){ + for (var i = 0, l = this.$headings.length; i < l; i++) { + this.$headings[i].setAttribute("options", value); + } + }; + + function scrollIntoView(){ + var Q = (this.current || this.$selected), + o = this.$container; + o.scrollTop = (Q.offsetTop) - 21; + } + + /**** Keyboard Support ****/ + + + /*function keyHandler(e){ + var key = e.keyCode, + ctrlKey = e.ctrlKey, + shiftKey = e.shiftKey, + selHtml = this.$selected || this.$caret; + + if (!e.force && (!selHtml || this.renaming)) //@todo how about allowdeselect? + return; + + var selXml = this.caret || this.selected, + oInt = this.$useiframe ? this.oDoc.documentElement : this.$container, + margin, node, hasScroll, hasScrollX, hasScrollY, items, lines; + + switch (key) { + case 13: + if (this.$tempsel) + this.$selectTemp(); + + this.choose(selHtml); + break; + case 32: + if (ctrlKey || !this.isSelected(this.caret)) + this.select(this.caret, true); + return false; + case 109: + case 46: + //DELETE + if (this.disableremove) + return; + + if (this.celledit) { + this.rename(this.caret || this.selected, ""); + return; + } + + if (this.$tempsel) + this.$selectTemp(); + + this.remove(this.mode ? this.caret : null); //this.mode != "check" + break; + case 36: + //HOME + this.$setTempSelected (this.getFirstTraverseNode(), false, shiftKey); + this.$container.scrollTop = 0; + return false; + case 35: + //END + this.$setTempSelected (this.getLastTraverseNode(), false, shiftKey); + this.$container.scrollTop = this.$container.scrollHeight; + return false; + case 107: + //+ + if (this.more) + this.startMore(); + break; + case 37: + //LEFT + if (this.$tempsel) + this.$selectTemp(); + + if (this.cellselect) { + if (this.$lastcell) { + if (this.$lastcell.previousSibling) { + this.selectCell({target:this.$lastcell.previousSibling}, + this.$selected); + } + } + else { + this.selectCell({target:this.$selected.firstChild}, + this.$selected); + } + } + else if (this.$withContainer) + this.slideToggle(this.$caret || this.$selected, 2) + return false; + case 107: + case 39: + //RIGHT + if (this.$tempsel) + this.$selectTemp(); + + if (this.cellselect) { + if (this.$lastcell) { + if (this.$lastcell.nextSibling) { + this.selectCell({target:this.$lastcell.nextSibling}, + this.$selected); + } + } + else { + this.selectCell({target:this.$selected.firstChild}, + this.$selected); + } + } + else if (this.$withContainer) + this.slideToggle(this.$caret || this.$selected, 1) + + return false; + case 38: + //UP + if (!selXml && !this.$tempsel) + return false; + + node = this.$tempsel + ? apf.xmldb.getNode(this.$tempsel) + : selXml; + + margin = apf.getBox(apf.getStyle(selHtml, "margin")); + hasScroll = oInt.scrollHeight > oInt.offsetHeight; + items = Math.floor((oInt.offsetWidth + - (hasScroll ? 15 : 0)) / (selHtml.offsetWidth + + margin[1] + margin[3])); + + node = this.getNextTraverseSelected(node, false, items); + if (node) + this.$setTempSelected (node, ctrlKey, shiftKey); + else + return false; + + selHtml = apf.xmldb.findHtmlNode(node, this); + if (selHtml.offsetTop <= oInt.scrollTop) { + oInt.scrollTop = (Array.prototype.indexOf.call(this.getTraverseNodes(), node) < items + ? 0 + : selHtml.offsetTop - margin[0]) + - parseInt(apf.getStyle(oInt, "paddingTop")); + } + return false; + case 40: + //DOWN + if (!selXml && !this.$tempsel) + return false; + + node = this.$tempsel + ? apf.xmldb.getNode(this.$tempsel) + : selXml; + + margin = apf.getBox(apf.getStyle(selHtml, "margin")); + hasScroll = oInt.scrollHeight > oInt.offsetHeight; + items = Math.floor((oInt.offsetWidth + - (hasScroll ? 15 : 0)) / (selHtml.offsetWidth + + margin[1] + margin[3])); + + node = this.getNextTraverseSelected(node, true, items); + if (node) + this.$setTempSelected (node, ctrlKey, shiftKey); + else + return false; + + selHtml = apf.xmldb.findHtmlNode(node, this); + if (selHtml.offsetTop + selHtml.offsetHeight + > oInt.scrollTop + oInt.offsetHeight) // - (hasScroll ? 10 : 0) + oInt.scrollTop = selHtml.offsetTop + - oInt.offsetHeight + selHtml.offsetHeight + + margin[0]; //+ (hasScroll ? 10 : 0) + + return false; + case 33: + //PGUP + if (!selXml && !this.$tempsel) + return false; + + node = this.$tempsel + ? apf.xmldb.getNode(this.$tempsel) + : selXml; + + margin = apf.getBox(apf.getStyle(selHtml, "margin")); + hasScrollY = oInt.scrollHeight > oInt.offsetHeight; + hasScrollX = oInt.scrollWidth > oInt.offsetWidth; + items = Math.floor((oInt.offsetWidth + - (hasScrollY ? 15 : 0)) / (selHtml.offsetWidth + + margin[1] + margin[3])) || 1; + lines = Math.floor((oInt.offsetHeight + - (hasScrollX ? 15 : 0)) / (selHtml.offsetHeight + + margin[0] + margin[2])); + + node = this.getNextTraverseSelected(node, false, items * lines); + if (!node) + node = this.getFirstTraverseNode(); + if (node) + this.$setTempSelected (node, ctrlKey, shiftKey); + else + return false; + + selHtml = apf.xmldb.findHtmlNode(node, this); + if (selHtml.offsetTop < oInt.scrollTop) { + oInt.scrollTop = (Array.prototype.indexOf.call(this.getTraverseNodes(), node) < items + ? 0 + : selHtml.offsetTop - margin[0]) + - parseInt(apf.getStyle(oInt, "paddingTop")); + } + return false; + case 34: + //PGDN + if (!selXml && !this.$tempsel) + return false; + + node = this.$tempsel + ? apf.xmldb.getNode(this.$tempsel) + : selXml; + + margin = apf.getBox(apf.getStyle(selHtml, "margin")); + hasScrollY = oInt.scrollHeight > oInt.offsetHeight; + hasScrollX = oInt.scrollWidth > oInt.offsetWidth; + items = Math.floor((oInt.offsetWidth - (hasScrollY ? 15 : 0)) + / (selHtml.offsetWidth + margin[1] + margin[3])) || 1; + lines = Math.floor((oInt.offsetHeight - (hasScrollX ? 15 : 0)) + / (selHtml.offsetHeight + margin[0] + margin[2])); + + node = this.getNextTraverseSelected(selXml, true, items * lines); + if (!node) + node = this.getLastTraverseNode(); + if (node) + this.$setTempSelected (node, ctrlKey, shiftKey); + else + return false; + + selHtml = apf.xmldb.findHtmlNode(node, this); + if (selHtml.offsetTop + selHtml.offsetHeight + > oInt.scrollTop + oInt.offsetHeight) // - (hasScrollY ? 10 : 0) + oInt.scrollTop = selHtml.offsetTop + - oInt.offsetHeight + selHtml.offsetHeight + + margin[0]; //+ 10 + (hasScrollY ? 10 : 0) + return false; + default: + if (this.celledit) { + if (!ctrlKey && !e.altKey && (key > 46 && key < 112 || key > 123)) + this.startRename(null, true); + return; + } + else if (key == 65 && ctrlKey) { + this.selectAll(); + return false; + } + //@todo make this work with the sorted column + else if (this.caption || (this.bindingRules || {})["caption"]) { + if (!this.xmlRoot) return; + + //this should move to a onkeypress based function + if (!this.lookup || new Date().getTime() + - this.lookup.date.getTime() > 300) + this.lookup = { + str : "", + date : new Date() + }; + + this.lookup.str += String.fromCharCode(key); + + var nodes = this.getTraverseNodes(); //@todo start at current indicator + for (var v, i = 0; i < nodes.length; i++) { + v = this.$applyBindRule("caption", nodes[i]); + if (v && v.substr(0, this.lookup.str.length) + .toUpperCase() == this.lookup.str) { + + if (!this.isSelected(nodes[i])) { + if (this.mode == "check") + this.setCaret(nodes[i]); + else + this.select(nodes[i]); + } + + if (selHtml) + this.$container.scrollTop = selHtml.offsetTop + - (this.$container.offsetHeight + - selHtml.offsetHeight) / 2; + return; + } + } + return; + } + break; + }; + + this.lookup = null; + //return false; + }*/ + + + + /**** Focus ****/ + // Too slow for IE + + + this.$getCaptionElement = function(){ + if (!this.$selected) + return false; + + var nodes = this.$head.childNodes, + htmlNodes = this.$selected.childNodes, i = 0; + + nodeIter = htmlNodes[i]; + while (nodeIter) { + if (nodeIter.nodeType != 1) { + nodeIter = nodeIter.nextSibling; + continue; + } + + h = apf.all[nodes[i].getAttribute("hid")]; + if (h.tree || h.rename) + return this.$getLayoutNode(h.tree ? "treecell" : "cell", "caption", nodeIter) || nodeIter; + + i++; + nodeIter = nodeIter.nextSibling; + } + + throw new Error("Datagrid without rename column specified"); + }; + + + this.$focus = function(){ + if (!this.$ext || (apf.isIE && this.$useiframe && this.cssfix)) //@todo fix this by fixing focussing for this component + return; + + this.$setStyleClass(this.$ext, this.$baseCSSname + "Focus"); + + if (this.oDoc) + this.$setStyleClass(this.oDoc.documentElement, this.$baseCSSname + "Focus"); + }; + + this.$blur = function(){ + //@todo fix this by fixing focussing for this component + if (!this.$ext || (apf.isIE && this.$useiframe && this.cssfix)) + return; + + this.$setStyleClass(this.$ext, "", [this.$baseCSSname + "Focus"]); + + if (this.oDoc) + this.$setStyleClass(this.oDoc.documentElement, "", [this.$baseCSSname + "Focus"]); + + hideEditor.call(this); + }; + + /**** Databinding ****/ + + this.addEventListener("bindingsload", this.$loaddatabinding = function(e){ + var rules = e.bindings["column"]; + if (!rules || !rules.length) + return; + + this.$cssRules = []; + this.$headings = rules; + + this.clearAllCache(); + + var fixed = 0, found = false; + for (var h, i = 0, l = rules.length; i < l; i++) { + h = rules[i]; + + + + if (h.visible !== false) { + if (!h.$isPercentage) + fixed += parseFloat(h.$width) || 0; + else + found = true; + } + } + + if (!found) { //@todo removal??? + this.$isFixedGrid = true; + this.$setStyleClass(this.$ext, "fixed"); + + if (this.$useiframe) + this.$setStyleClass(this.oDoc.documentElement, "fixed"); + } + else { + //@todo remove + } + + if (fixed > 0 && !this.$isFixedGrid) { + var vLeft = fixed; + + //first column has total -1 * fixed margin-left. - 5 + //cssRules[0][1] += ";margin-left:-" + vLeft + "px;"; + //cssRules[1][1] += ";margin-left:-" + vLeft + "px;"; + this.$cssRules.push(["." + this.$baseCSSname + " .row" + this.$uniqueId, + "padding-right:" + vLeft + "px;margin-right:-" + vLeft + "px"]); + + //headings and records have same padding-right + this.$container.style.paddingRight = (vLeft - 1) + "px"; + this.$head.style.paddingRight = (vLeft - 2) + "px"; + } + + this.$fixed = fixed; + this.$first = 0; + + this.$withContainer = e.bindings.description ? true : false; + + //Activate CSS Rules + apf.importStylesheet(this.$cssRules, window); + + if (this.$useiframe) + apf.importStylesheet(this.$cssRules, this.oWin); + }); + + this.$initNode = function(xmlNode, state, Lid, depth){ + //Build Row + this.$getNewContext("item"); + var oRow = this.$getLayoutNode("item"); + oRow.setAttribute("id", Lid); + + //@todo if treearch + oRow.setAttribute("class", oRow.getAttribute("class") + " " + + treeState[state] + " item" + this.$uniqueId);//"width:" + (totalWidth+40) + "px"); + this.$setStyleClass(this.$getLayoutNode("item", "container"), treeState[state]) + + oRow.setAttribute("ondblclick", 'var o = apf.lookup(' + this.$uniqueId + ');o.choose(null, true);' + + (this.$withContainer ? 'o.slideToggle(this, null, true);' : '') + + (this.celledit && !this.namevalue ? 'o.startRename(null, null, true);' : '')); + + if (this.hasFeature(apf.__DRAGDROP__)) { + oRow.setAttribute("onmouseout", 'this.hasPassedDown = false;'); + oRow.setAttribute("onmousedown", 'var o = apf.lookup(' + this.$uniqueId + ');\ + var xmlNode = apf.xmldb.findXmlNode(this);\ + var isSelected = o.isSelected(xmlNode);\ + this.hasPassedDown = true;\ + if (!o.hasFeature(apf.__DRAGDROP__) || !isSelected && !apf.getCtrlKey(event))\ + o.select(this, apf.getCtrlKey(event), event.shiftKey, -1);' + + (this.cellselect || this.namevalue ? 'o.selectCell(event, this, isSelected);' : '')); + + oRow.setAttribute("onmouseup", 'if (!this.hasPassedDown) return;\ + var o = apf.lookup(' + this.$uniqueId + ');\ + var xmlNode = apf.xmldb.findXmlNode(this);\ + var isSelected = o.isSelected(xmlNode);\ + if (o.hasFeature(apf.__DRAGDROP__))\ + o.select(this, apf.getCtrlKey(event), event.shiftKey, -1);'); + } //@todo add DRAGDROP ifdefs + else { + oRow.setAttribute("onmousedown", 'var o = apf.lookup(' + this.$uniqueId + ');\ + var wasSelected = o.$selected == this;\ + o.select(this, apf.getCtrlKey(event), event.shiftKey, -1);' + + (this.cellselect || this.namevalue ? 'o.selectCell(event, this, wasSelected);' : '')); + } + + //Build the Cells + for (var cellType, cell, h, i = 0; i < this.$headings.length; i++) { + h = this.$headings[i]; + + if (h.tree && h.check) { + cellType = "treecheckcell"; + + this.$getNewContext("treecheckcell"); + cell = this.$getLayoutNode("treecheckcell"); + var oc = this.$getLayoutNode("treecheckcell", "openclose"); + oc.setAttribute("style", "margin-left:" + (((depth||0)) * 15 + 4) + "px;"); + oc.setAttribute("onmousedown", + "var o = apf.lookup(" + this.$uniqueId + ");\ + o.slideToggle(this, null, null, true);\ + event.cancelBubble = true;\ + apf.window.$mousedown(event);"); + + oc.setAttribute("ondblclick", "event.cancelBubble = true"); + } + else if (h.tree) { + cellType = "treecell"; + + this.$getNewContext("treecell"); + cell = this.$getLayoutNode("treecell"); + var oc = this.$getLayoutNode("treecell", "openclose"); + oc.setAttribute("style", "margin-left:" + (((depth||0)) * 15 + 4) + "px;"); + oc.setAttribute("onmousedown", + "var o = apf.lookup(" + this.$uniqueId + ");\ + o.slideToggle(this, null, null, true);\ + event.cancelBubble = true;\ + apf.window.$mousedown(event);"); + + oc.setAttribute("ondblclick", "event.cancelBubble = true"); + + /*cell.setAttribute("style", "background-position: " + + ((((depth||0)+1) * 15) - 10) + "px 50%");*/ + } + else { + + cellType = h.check ? "checkcell" : "cell"; + + + this.$getNewContext(cellType); + cell = this.$getLayoutNode(cellType); + } + + + if (this.$mode && h.check) { + var elCheck = this.$getLayoutNode(cellType, "check"); + if (elCheck) { + elCheck.setAttribute("onmousedown", + "var o = apf.lookup(" + this.$uniqueId + ");\ + o.checkToggle(this, true);\o.$skipSelect = true;"); + + if (apf.isTrue(this.$applyBindRule("checked", xmlNode))) { + this.$checkedList.push(xmlNode); + this.$setStyleClass(oRow, "checked"); + } + else if (this.isChecked(xmlNode)) + this.$setStyleClass(oRow, "checked"); + } + else { + + return false; + } + } + + + apf.setStyleClass(cell, h.$className); + + if (h.css) + apf.setStyleClass(cell, (apf.lm.compile(h.css))(xmlNode)); //@todo cashing of compiled function? + + if (h.icon) { + var node = this.$getLayoutNode(cellType, "caption", oRow.appendChild(cell)); + node = (node.nodeType == 1 && node || node.parentNode); + node.setAttribute("style", "background-image:url(" + + apf.getAbsolutePath(this.iconPath, + ((h.cicon || h.$compile("icon", {nostring: true}))(xmlNode) || "")) + + ");"); + this.$setStyleClass(node, "iconCell", []); + } + + if (h.value) { + if (!h.cvalue2) { + h.$compile("value", {nostring: true}); + + + if (h.value) + h.cvalue2.hasAml = h.value.indexOf(" -1; + + } + + + if (h.cvalue2.hasAml){ + var q = (this.$amlBindQueue || (this.$amlBindQueue = {})); + + var htmlEl = this.$getLayoutNode(cellType, + "caption", oRow.appendChild(cell)); + htmlEl.setAttribute("id", "placeholder_" + this.$uniqueId + + "_" + ((q.column || (q.column = [])).push(xmlNode) - 1)); + apf.setNodeValue(htmlEl, ' '); + } + else + + { + apf.setNodeValue(this.$getLayoutNode(cellType, + "caption", oRow.appendChild(cell)), + h.cvalue2(xmlNode) || ' '); + } + } + } + + if (this.$bindings && this.$bindings.color) { + var colorRule = this.$getDataNode("color", xmlNode); + this.$setStyleClass(oRow, colorRule ? "highlight" : null, colorRule ? ["highlight"] : null); + } + + + var cssClass = this.$applyBindRule("css", xmlNode); + if (cssClass) { + this.$setStyleClass(oRow, cssClass); + if (cssClass) + this.$dynCssClasses.push(cssClass); + } + + + /*if (this.$withContainer) { + var desc = this.$applyBindRule("description", xmlNode); + this.$getNewContext("container"); + var oDesc = this.$getLayoutNode("container"); + apf.setNodeValue(this.$getLayoutNode("container", "container", + oDesc), desc); + oDesc.setAttribute("class", (oDesc.getAttribute("class") || "") + + " row" + this.$uniqueId); + + if (htmlParentNode) + apf.insertHtmlNode(oDesc, htmlParentNode, beforeNode); + else + this.$nodes.push(oDesc); + }*/ + + return oRow; + }; + + this.$updateNode = function(xmlNode, htmlNode){ + if (!htmlNode) return; + + var nodes = this.$head.childNodes, + htmlNodes = htmlNode.childNodes, + cell, p; + + if (!this.namevalue && this.$curBtn) + p = this.$curBtn.parentNode; + + var nodeIter, h, i = 0; + nodeIter = htmlNodes[0]; + while (nodeIter) { + if (nodeIter.nodeType != 1) { + nodeIter = nodeIter.nextSibling; + continue; + } + + h = apf.all[nodes[i].getAttribute("hid")]; + + //@todo fake optimization + cell = this.$getLayoutNode(h.tree ? "treecell" : "cell", "caption", nodeIter) || nodeIter;//htmlNodes[i].firstChild || + + if (h.css) + apf.setStyleClass(cell, (apf.lm.compile(h.css))(xmlNode)); //@todo cashing of compiled function? + + if (h.tree) { + + //@todo + + + /*var oc = this.$getLayoutNode("treecell", "openclose", cell); + oc.setAttribute("style", "margin-left:" + (((depth||0)) * 15 + 4) + "px;"); + oc.setAttribute("onmousedown", + "var o = apf.lookup(" + this.$uniqueId + ");\ + o.slideToggle(this, null, null, true);");*/ + } + + if (h.value) { + if (!h.cvalue2) { + h.$compile("value", {nostring: true}); + + + if (h.value) + h.cvalue2.hasAml = h.value.indexOf(" -1; + + } + + + if (!h.cvalue2.hasAml) + + cell.innerHTML = h.cvalue2(xmlNode) || " "; + } + + if (h.icon) { + (cell.nodeType == 1 && cell || cell.parentNode).style.backgroundImage = + "url(" + apf.getAbsolutePath(this.iconPath, + ((h.cicon || h.$compile("icon", {nostring: true}))(xmlNode) || "")) + + ")"; + } + + i++; + nodeIter = nodeIter.nextSibling; + } + + //return; //@todo fake optimization + + if (this.$bindings && this.$bindings.color) { + var colorRule = this.$getDataNode("color", xmlNode); + this.$setStyleClass(htmlNode, colorRule ? "highlight" : null, + colorRule ? ["highlight"] : null); + } + + + var cssClass = this.$applyBindRule("css", xmlNode); + if (cssClass || this.$dynCssClasses.length) { + this.$setStyleClass(htmlNode, cssClass, this.$dynCssClasses); + if (cssClass && !this.$dynCssClasses.contains(cssClass)) + this.$dynCssClasses.push(cssClass); + } + + + /*if (this.$withContainer) { + htmlNode.nextSibling.innerHTML + = this.$applyBindRule("description", xmlNode) || ""; + }*/ + }; + + this.$dblclick = function(htmlNode){ + var _self = this, id, cell; + while (!(id = htmlNode.getAttribute(apf.xmldb.htmlIdTag)) || id.indexOf("|") == -1) { + htmlNode = (cell = htmlNode).parentNode; + if (!htmlNode || htmlNode.nodeType != 1) + return; + } + + if (this.$lastEditor && this.$lastEditor[3] == htmlNode) + return; + + var h, colId = cell.className.match(/(col\d+)/)[1]; + for (var i = 0; i < this.$headings.length; i++) { + if (this.$headings[i].$className == colId) { + h = this.$headings[i]; + break; + } + } + + if (!h.editor) //No editor specified + return; + + /*if (this.$lastEditor) { + //this.$lastEditor[0].$blur(); + this.$lastEditor[0].setProperty("visible", false); + + var nodes = this.$lastEditor[1].childNodes; + for (var i = 0, l = nodes.length; i < l; i++) { + if (!nodes[i].host) + nodes[i].style.display = ""; + } + }*/ + + var xmlNode = apf.xmldb.getNode(htmlNode); + /* + - editor (name of widget, lm function returning amlNode or lm template ref) + - children being aml nodes + */ + var editParent = h.tree + ? this.$getLayoutNode("cell", "caption", cell) + : cell; + + var oEditor, editor = h.editor; + var ceditor = apf.lm.compile(editor, {xpathmode: 2}); //@todo can this be more efficient? + + var nodes = editParent.childNodes; + for (var i = 0, l = nodes.length; i < l; i++) { + if (!nodes[i].host) { + if (nodes[i].nodeType == 1) + nodes[i].style.display = "none"; + else { + this.$lastTextValue = nodes[i].nodeValue; + nodes[i].nodeValue = ""; //@todo + } + } + } + + if (ceditor.type == 2) { + if (!this.$editors[h.$uniqueId + ":" + editor]) { + var constr = apf.namespaces[apf.ns.aml].elements[editor]; + var info = { + htmlNode : editParent, + style : "position:relative;z-index:10000", + value : "[{" + this.id + ".selected}::" + + (v = h.value).substr(1, v.length - 2) //only xpath value's supported for now + + "]", + focussable : false + }; + + if (h.tree) + info.width = "100% - " + (editParent.offsetLeft + parseInt(this.$getOption("treecell", "editoroffset"))); + else + info.width = "100%-3"; + + //@todo copy all non-known properties of the prop element + + if (constr.prototype.hasFeature(apf.__MULTISELECT__)) { + info.caption = h.eachcaption || "[text()]"; + info.eachvalue = h.eachvalue; // || "[@value]"; + + var model; + if (model = h.getAttribute("model")) { + info.model = model; + info.each = h.each; + } + else { + /*var each = h.each; + if (each.charAt(0) == "[") + each = each.substr(1, each.length - 2); + info.each = "[{" + this.id + ".selected}::" + each + "]";*/ + info.model = "{" + this.id + ".selected}" + info.each = h.each; + } + } + + if (h.skin) + info.skin = h.skin; + if (h["class"]) + info["class"] = h["class"]; + if (h.fill) + info.fill = h.fill; + + oEditor = this.$editors[h.$uniqueId + ":" + editor] = new constr(info); + + var box = apf.getBox(apf.getStyle(oEditor.$ext, "margin")); + if (box[1] || box[3]) { + oEditor.setAttribute("width", "100%+2-" + (box[1] + box[3])); + } + //else if (!box[3]) + //oEditor.$ext.style.marginLeft = "-1px"; + + //oEditor.$focussable = false; + oEditor.addEventListener("blur", function(){ + hideEditor.call(_self); + }); + oEditor.parentNode = this; + oEditor.realtime = false; + oEditor.$focusParent = this; + oEditor.setAttribute("focussable", "true"); + //delete oEditor.parentNode; + + oEditor.addEventListener("keydown", function(e){ + if (e.keyCode == 13) { + hideEditor.call(_self); + _self.$focus(); + } + else if (e.keyCode == 27) { + oEditor.removeAttribute("value"); //@todo this bugs in slider + hideEditor.call(_self); + //_self.getActionTracker().undo(); + } + }); + + //@todo set actiontracker + + //Patch oEditor to forward change + oEditor.$executeAction = function(){ + this.parentNode.$executeAction.apply(this.parentNode, arguments); + } + } + else { + oEditor = this.$editors[h.$uniqueId + ":" + editor]; + + if (oEditor.hasFeature(apf.__MULTISELECT__) && !h.model) { + //oEditor.setAttribute("model", "{" + this.id + ".selected}"); + /*var each = h.each; + if (each.charAt(0) == "[") + each = each.substr(1, each.length - 2); + oEditor.setAttribute("each", "[{" + this.id + ".selected}::" + each + "]");*/ + /*apf.queue.empty();*/ + oEditor.setAttribute("value", "[{" + this.id + ".selected}::" + + (v = h.value).substr(1, v.length - 2) + + "]"); + } + else { + oEditor.setAttribute("value", "[{" + this.id + ".selected}::" + + (v = h.value).substr(1, v.length - 2) + + "]"); + } + + oEditor.setProperty("visible", true); + editParent.appendChild(oEditor.$ext); + + oEditor.setAttribute("width", h.tree + ? "100% - " + (editParent.offsetLeft + parseInt(this.$getOption("treecell", "editoroffset"))) + : "100%-3"); + } + + /*setTimeout(function(){ + oEditor.focus(); + });*/ + } + else { + //Create dropdown + + var obj = ceditor.call(this, this.xmlRoot); + if (obj.localName == "template") { + //add template contents to dropped area + } + else { + //add xml into dropped area + } + } + + if (oEditor.localName == "textbox") + oEditor.select(); + + oEditor.focus(); + oEditor.$focus(); + + this.$setStyleClass(htmlNode, "editing"); + + this.$lastEditor = [oEditor, editParent, xmlNode, htmlNode, this.getActionTracker().undolength]; + } + + this.addEventListener("mousedown", function(e){ + if (this.$lastEditor + && !apf.isChildOf(this.$lastEditor[1], + e.htmlEvent.srcElement || e.htmlEvent.target, true)) + hideEditor.call(this); + }); + + var hideEditor = function(e){ + if (this.$lastEditor) { + var ed = this.$lastEditor; + this.$lastEditor = null; + + if (ed[0].hasFeature(apf.__MULTISELECT__)) // && !ed[0].model + ed[0].$clearDynamicProperty("value"); + + //this.$lastEditor[0].$blur(); + ed[0].setProperty("visible", false); + + var nodes = ed[1].childNodes; + for (var i = 0, l = nodes.length; i < l; i++) { + if (!nodes[i].host) { + if (nodes[i].nodeType == 1) + nodes[i].style.display = ""; + else if (!ed[0].value || ed[0].value == this.$lastTextValue) { + nodes[i].nodeValue = this.$lastTextValue; //@todo + } + } + } + + this.$setStyleClass(ed[3], "", ["editing"]); + + this.focus(); + } + }; + this.addEventListener("beforeselect", hideEditor); + + /**** Column management ****/ + + /** + * Returns a column definition object based on the column number. + * @param {Number} hid the heading number; this number is based on the sequence of the column elements. + */ + this.getColumn = function(nr){ + return this.$headings[nr || this.$lastcol || 0]; + }; + + /** + * Resizes a column. + * @param {Number} hid the heading number; this number is based on the sequence of the column elements. + * @param {Number} newsize the new size of the column. + * @todo optimize but bringing down the string concats + */ + this.resizeColumn = function(nr, newsize){ + var h = this.$headings[nr]; + h.resize(newsize); + }; + + /** + * Hides a column. + * @param {Number} hid the heading number; this number is based on the sequence of the column elements. + */ + this.hideColumn = function(nr){ + var h = this.$headings[nr]; + h.hide(); + }; + + /** + * Shows a hidden column. + * @param {Number} hid the heading number; this number is based on the sequence of the column elements. + */ + this.showColumn = function(nr){ + var h = this.$headings[nr]; + h.show(); + }; + + /** + * Sorts a column. + * @param {Number} hid the heading number; this number is based on the sequence of the column elements. + */ + this.sortColumn = function(hid){ + var h = this.$headings[nr]; + h.sort(); + }; + + /** + * Moves a column to another position. + * @param {Number} fromHid the heading number of the column to move; this number is based on the sequence of the column elements. + * @param {Number} toHid the position the column is moved to; + */ + this.moveColumn = function(from, to){ + var h = this.$headings[nr]; + h.move(to); + } + + /**** Init ****/ + + this.$draw = function(){ + this.$drawBase(); + + var _self = this; + this.$ext.onmousedown = function(e){ + _self.dispatchEvent("mousedown", {htmlEvent: e || event}); + } + + //@todo rename 'body' to 'container' + + //Build Main Skin + this.$head = this.$getLayoutNode("main", "head", this.$ext); + this.$pointer = this.$getLayoutNode("main", "pointer", this.$ext); + + if (this.$head.firstChild) + this.$head.removeChild(this.$head.firstChild); + if (this.$container.firstChild) + this.$container.removeChild(this.$container.firstChild); + + var widthdiff = this.$widthdiff = this.$getOption("main", "widthdiff") || 0; + this.$defaultwidth = this.$getOption("main", "defaultwidth") || "100"; + this.$useiframe = apf.isIE && (apf.isTrue(this.$getOption("main", "iframe")) || this.iframe); + + //Initialize Iframe + if (this.$useiframe && !this.oIframe) { + //this.$container.style.overflow = "hidden"; + //var sInt = this.$container.outerHTML + var sClass = this.$container.className; + //this.$container.parentNode.removeChild(this.$container); + this.oIframe = this.$container.appendChild(document.createElement(apf.isIE + ? "" + : "iframe")); + this.oIframe.frameBorder = 0; + this.oWin = this.oIframe.contentWindow; + this.oDoc = this.oWin.document; + this.oDoc.write('\ + \ + \ + \ + \ + '); + //Import CSS + //this.oDoc.body.innerHTML = sInt; + this.$container = this.oDoc.body;//.firstChild; + this.$container.className = sClass;//this.oIframe.parentNode.className; + this.oDoc.documentElement.className = this.$ext.className; + //this.oDoc.body.className = this.$ext.className; + + apf.skins.loadCssInWindow(this.skinName, this.oWin, this.mediaPath, this.iconPath); + + if (apf.isIE) //@todo this can be removed when focussing is fixed for this component + this.$setStyleClass(this.oDoc.documentElement, this.$baseCSSname + "Focus"); + + apf.convertIframe(this.oIframe, true); + + if (apf.getStyle(this.oDoc.documentElement, "overflowY") == "auto") { + //@todo ie only + this.oIframe.onresize = function(){ + _self.$head.style.marginRight = + _self.oDoc.documentElement.scrollHeight > _self.oDoc.documentElement.offsetHeight + ? "16px" : "0"; + } + + this.addEventListener("afterload", this.oIframe.onresize); + this.addEventListener("xmlupdate", this.oIframe.onresize); + } + + this.oDoc.documentElement.onscroll = + function(){ + if (_self.$isFixedGrid) + _self.$head.scrollLeft = _self.oDoc.documentElement.scrollLeft; + }; + } + else { + if (apf.getStyle(this.$container, "overflowY") == "auto") { + this.$resize = function(){ + _self.$head.style.marginRight = + _self.$container.scrollHeight > _self.$container.offsetHeight + ? "16px" : "0"; + } + + + apf.layout.setRules(this.$ext, this.$uniqueId + "_datagrid", + "var o = apf.all[" + this.$uniqueId + "];\ + if (o) o.$resize()"); + apf.layout.queue(this.$ext); + + + this.addEventListener("afterload", this.$resize); + this.addEventListener("xmlupdate", this.$resize); + } + + this.$container.onscroll = + function(){ + if (_self.$isFixedGrid) + _self.$head.scrollLeft = _self.$container.scrollLeft; + }; + } + + this.$container[this.clickedit ? "onmousedown" : "ondblclick"] = function(e){ + if (!e) e = event; + _self.$dblclick(e.srcElement || e.target); + } + }; + + this.$destroy = function(){ + //@todo destroy this.$txt here + + this.$ext.onclick = this.$container.onresize = null; + + + apf.layout.removeRule(this.$container, "dg" + this.$uniqueId); + apf.layout.activateRules(this.$container); + + }; +}).call(apf.datagrid.prototype = new apf.BaseTree()); + +apf.aml.setElement("datagrid", apf.datagrid); +//apf.aml.setElement("column", apf.BindingRule); +apf.aml.setElement("description", apf.BindingRule); +apf.aml.setElement("color", apf.BindingRule); +apf.aml.setElement("contents", apf.BindingRule); + + + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/elements/defaults.js)SIZE(1838)TIME(Fri, 14 Oct 2011 08:01:44 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + + +/** + * + * @author Ruben Daniels (ruben AT ajax DOT org) + * @version %I%, %G% + * @since 0.4 + */ +apf.defaults = function(struct, tagName){ + this.$init(tagName || "services", apf.NODE_HIDDEN, struct); +}; + +(function(){ + this.$parsePrio = "002"; + + this.$propHandlers["for"] = function(value){ + if (this.$lastFor) + apf.nameserver.remove("defaults_" + this.$lastFor, this); + + apf.nameserver.add("defaults_" + value, this); + this.$lastFor = value; + } + + //@todo apf3.x how should this work? + this.addEventListener("DOMNodeRemovedFromDocument", function(e){ + apf.nameserver.remove("defaults_" + this.$lastFor, this); + }); + +}).call(apf.defaults.prototype = new apf.AmlElement()); + +apf.aml.setElement("defaults", apf.defaults); + + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/elements/divider.js)SIZE(2833)TIME(Wed, 30 Nov 2011 17:29:20 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + +/** + * Element displaying a divider. For use in toolbars, menu's and such. + * @define divider + * @constructor + */ +apf.divider = function(struct, tagName){ + this.$init(tagName || "divider", apf.NODE_VISIBLE, struct); +}; + +(function() { + this.$focussable = false; + + this.implement(apf.ChildValue); + this.$childProperty = "caption"; + + //@todo apf3.0 fix this + this.addEventListener("AMLReparent", function(beforeNode, pNode, withinParent){ + if (!this.$amlLoaded) + return; + + if (!withinParent && this.skinName != pNode.skinName) { + //@todo for now, assuming dom garbage collection doesn't leak + this.loadAml(); + } + }); + + /** + * @attribute {String} caption the text displayed in the area defined by this + * element. + */ + this.$supportedProperties.push("caption", "value", "for", "textalign"); + this.$propHandlers["caption"] = function(value){ + if (this.$caption) { + this.$setStyleClass(this.$ext, this.$baseCSSname + "Caption"); + this.$caption.innerHTML = value; + } + else { + this.$setStyleClass(this.$ext, "", [this.$baseCSSname + "Caption"]); + } + }; + + this.$canLeechSkin = true; + + /** + * @private + */ + this.$draw = function() { + if (this.parentNode.isPaged && this.parentNode.$buttons) + this.$pHtmlNode = this.parentNode.$buttons; + + if (this.$isLeechingSkin) { + this.$ext = apf.insertHtmlNode( + this.parentNode.$getLayoutNode("divider"), this.$pHtmlNode); + } + else { + this.$ext = this.$getExternal("main"); + this.$caption = this.$getLayoutNode("main", "caption", this.$ext); + } + }; +}).call(apf.divider.prototype = new apf.Presentation); + +apf.aml.setElement("divider", apf.divider); + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/elements/dropdown.js)SIZE(15384)TIME(Tue, 06 Dec 2011 16:22:54 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + + +/** + * Element allowing a user to select a value from a list, which is + * displayed when the user clicks a button. + * Example: + * A simple dropdown with inline items. + * + * + * The Netherlands + * United States of America + * United Kingdom + * ... + * + * + * Example: + * A databound dropdown with items loaded from an xml file. + * + * + * + * Example: + * A databound dropdown using the bindings element + * + * + * + * + * + * + * + * + * + * Example: + * A small form. + * + * + * + * Mike + * amsterdam + * + * + * + * + * Name + * + * + * City + * + * + * + * + * + * + * + * + * Submit + * + * + * + * @event slidedown Fires when the calendar slides open. + * cancelable: Prevents the calendar from sliding open + * @event slideup Fires when the calendar slides up. + * cancelable: Prevents the calendar from sliding up + * + * @constructor + * @define dropdown + * @allowchild item, {smartbinding} + * @addnode elements + * + * @inherits apf.BaseList + * + * @author Ruben Daniels (ruben AT ajax DOT org) + * @version %I%, %G% + * @since 0.4 + */ +apf.dropdown = function(struct, tagName){ + this.$init(tagName || "dropdown", apf.NODE_VISIBLE, struct); +}; + +(function(){ + this.$animType = 1; + this.$animSteps = 5; + this.$animSpeed = 20; + this.$itemSelectEvent = "onmouseup"; + + /**** Properties and Attributes ****/ + + this.dragdrop = false; + this.reselectable = true; + this.$focussable = apf.KEYBOARD; + this.autoselect = false; + this.multiselect = false; + this.disableremove = true; + this.delayedselect = false; + this.maxitems = 5; + + this.$booleanProperties["disableremove"] = true; + this.$supportedProperties.push("maxitems", "disableremove", + "initial-message", "fill"); + + /** + * @attribute {String} initial-message the message displayed by this element + * when it doesn't have a value set. This property is inherited from parent + * nodes. When none is found it is looked for on the appsettings element. + * + * @attribute {Number} maxitems the number of items that are shown at the + * same time in the container. + */ + this.$propHandlers["maxitems"] = function(value){ + this.sliderHeight = value + ? (Math.min(this.maxitems || 100, value) * this.itemHeight) + : 10; + this.containerHeight = value + ? (Math.min(this.maxitems || 100, value) * this.itemHeight) + : 10; + /*if (this.containerHeight > 20) + this.containerHeight = Math.ceil(this.containerHeight * 0.9);*/ + }; + + this.addEventListener("prop.class", function(e){ + this.$setStyleClass(this.oSlider, e.value); + }); + + /**** Public methods ****/ + + /** + * Toggles the visibility of the container with the list elements. It opens + * or closes it using a slide effect. + * @private + */ + this.slideToggle = function(e, userAction){ + if (!e) e = event; + if (userAction && this.disabled) + return; + + if (this.isOpen) + this.slideUp(); + else + this.slideDown(e); + }; + + /** + * Shows the container with the list elements using a slide effect. + * @private + */ + this.slideDown = function(e){ + if (this.dispatchEvent("slidedown") === false) + return false; + + this.isOpen = true; + + this.$propHandlers["maxitems"].call(this, this.xmlRoot && this.each + ? this.getTraverseNodes().length : this.childNodes.length); //@todo apf3.0 count element nodes + + this.oSlider.style.display = "block"; + if (!this.ignoreOverflow) { + this.oSlider.style[apf.supportOverflowComponent + ? "overflowY" + : "overflow"] = "visible"; + this.$container.style.overflowY = "hidden"; + } + + this.oSlider.style.display = ""; + + this.$setStyleClass(this.$ext, this.$baseCSSname + "Down"); + + //var pos = apf.getAbsolutePosition(this.$ext); + this.oSlider.style.height = (this.sliderHeight - 1) + "px"; + this.oSlider.style.width = (this.$ext.offsetWidth - 2 - this.widthdiff) + "px"; + + var _self = this; + var _popupCurEl = apf.popup.getCurrentElement(); + apf.popup.show(this.$uniqueId, { + x : 0, + y : this.$ext.offsetHeight, + animate : true, + container : this.$getLayoutNode("container", "contents", this.oSlider), + ref : this.$ext, + width : this.$ext.offsetWidth - this.widthdiff, + height : this.containerHeight, + allowTogether : (_popupCurEl && apf.isChildOf(_popupCurEl.$ext, _self.$ext)), + callback : function(container){ + if (!_self.ignoreOverflow) { + _self.$container.style.overflowY = "auto"; + } + } + }); + }; + + /** + * Hides the container with the list elements using a slide effect. + * @private + */ + this.slideUp = function(){ + if (this.isOpen == 2) return false; + if (this.dispatchEvent("slideup") === false) return false; + + this.isOpen = false; + if (this.selected) { + var htmlNode = apf.xmldb.findHtmlNode(this.selected, this); + if(htmlNode) this.$setStyleClass(htmlNode, '', ["hover"]); + } + + this.$setStyleClass(this.$ext, '', [this.$baseCSSname + "Down"]); + apf.popup.hide(); + return false; + }; + + /**** Private methods and event handlers ****/ + + //@todo apf3.0 why is this function called 6 times on init. + this.$setLabel = function(value){ + + this.oLabel.innerHTML = value || this["initial-message"] || ""; + + + this.$setStyleClass(this.$ext, value ? "" : this.$baseCSSname + "Initial", + !value ? [] : [this.$baseCSSname + "Initial"]); + }; + + this.addEventListener("afterselect", function(e){ + if (!e) e = event; + + this.slideUp(); + if (!this.isOpen) + this.$setStyleClass(this.$ext, "", [this.$baseCSSname + "Over"]); + + this.$setLabel(e.selection.length + ? this.$applyBindRule("caption", this.selected) + : ""); + }); + + function setMaxCount() { + if (this.isOpen == 2) + this.slideDown(); + } + + this.addEventListener("afterload", setMaxCount); + this.addEventListener("xmlupdate", function(){ + setMaxCount.call(this); + this.$setLabel(this.$applyBindRule("caption", this.selected)); + }); + + // Private functions + this.$blur = function(){ + this.slideUp(); + //this.$ext.dispatchEvent("mouseout") + if (!this.isOpen) + this.$setStyleClass(this.$ext, "", [this.$baseCSSname + "Over"]) + + this.$setStyleClass(this.$ext, "", [this.$baseCSSname + "Focus"]); + }; + + /*this.$focus = function(){ + apf.popup.forceHide(); + this.$setStyleClass(this.oFocus || this.$ext, this.$baseCSSname + "Focus"); + }*/ + + this.$setClearMessage = function(msg){ + this.$setLabel(msg); + }; + + this.$removeClearMessage = function(){ + this.$setLabel(""); + }; + + this.addEventListener("popuphide", this.slideUp); + + /**** Keyboard Support ****/ + + + this.addEventListener("keydown", function(e){ + var key = e.keyCode; + //var ctrlKey = e.ctrlKey; << unused + //var shiftKey = e.shiftKey; + + if (!this.xmlRoot) return; + + var node; + + switch (key) { + case 32: + this.slideToggle(e.htmlEvent); + break; + case 38: + //UP + if (e.altKey) { + this.slideToggle(e.htmlEvent); + return; + } + + if (!this.selected) + return; + + node = this.getNextTraverseSelected(this.caret + || this.selected, false); + + if (node) + this.select(node); + break; + case 40: + //DOWN + if (e.altKey) { + this.slideToggle(e.htmlEvent); + return; + } + + if (!this.selected) { + node = this.getFirstTraverseNode(); + if (!node) + return; + } + else + node = this.getNextTraverseSelected(this.selected, true); + + if (node) + this.select(node); + + break; + default: + if (key == 9 || !this.xmlRoot) return; + + //if(key > 64 && key < + if (!this.lookup || new Date().getTime() - this.lookup.date.getTime() > 1000) + this.lookup = { + str : "", + date : new Date() + }; + + this.lookup.str += String.fromCharCode(key); + + var caption, nodes = this.getTraverseNodes(); + for (var i = 0; i < nodes.length; i++) { + caption = this.$applyBindRule("caption", nodes[i]); + if (caption && caption.indexOf(this.lookup.str) > -1) { + this.select(nodes[i]); + return; + } + } + return; + } + + return false; + }, true); + + + /**** Init ****/ + + this.$draw = function(){ + this.$getNewContext("main"); + this.$getNewContext("container"); + + this.$animType = this.$getOption("main", "animtype") || 1; + this.clickOpen = this.$getOption("main", "clickopen") || "button"; + + //Build Main Skin + this.$ext = this.$getExternal(null, null, function(oExt){ + oExt.setAttribute("onmouseover", 'var o = apf.lookup(' + this.$uniqueId + + ');o.$setStyleClass(o.$ext, o.$baseCSSname + "Over", null, true);'); + oExt.setAttribute("onmouseout", 'var o = apf.lookup(' + this.$uniqueId + + ');if(o.isOpen) return;o.$setStyleClass(o.$ext, "", [o.$baseCSSname + "Over"], true);'); + + //Button + var oButton = this.$getLayoutNode("main", "button", oExt); + if (oButton) { + oButton.setAttribute("onmousedown", 'apf.lookup(' + + this.$uniqueId + ').slideToggle(event, true);'); + } + + //Label + var oLabel = this.$getLayoutNode("main", "label", oExt); + if (this.clickOpen == "both") { + oLabel.parentNode.setAttribute("onmousedown", 'apf.lookup(' + + this.$uniqueId + ').slideToggle(event, true);'); + } + }); + this.oLabel = this.$getLayoutNode("main", "label", this.$ext); + + + if (this.oLabel.nodeType == 3) + this.oLabel = this.oLabel.parentNode; + + + this.oIcon = this.$getLayoutNode("main", "icon", this.$ext); + if (this.$button) + this.$button = this.$getLayoutNode("main", "button", this.$ext); + + this.oSlider = apf.insertHtmlNode(this.$getLayoutNode("container"), + document.body); + this.$container = this.$getLayoutNode("container", "contents", this.oSlider); + this.$container.host = this; + + //Set up the popup + this.$pHtmlDoc = apf.popup.setContent(this.$uniqueId, this.oSlider, + apf.skins.getCssString(this.skinName)); + + //Get Options form skin + //Types: 1=One dimensional List, 2=Two dimensional List + this.listtype = parseInt(this.$getLayoutNode("main", "type")) || 1; + + this.itemHeight = this.$getOption("main", "item-height") || 18.5; + this.widthdiff = this.$getOption("main", "width-diff") || 0; + this.ignoreOverflow = apf.isTrue(this.$getOption("main", "ignore-overflow")) || false; + }; + + this.addEventListener("DOMNodeInsertedIntoDocument", function(){ + if (typeof this["initial-message"] == "undefined") + this.$setInheritedAttribute("initial-message"); + + if (!this.selected && this["initial-message"]) + this.$setLabel(); + }); + + this.$destroy = function(){ + apf.popup.removeContent(this.$uniqueId); + apf.destroyHtmlNode(this.oSlider); + this.oSlider = null; + }; + + + this.$getActiveElements = function() { + // init $activeElements + if (!this.$activeElements) { + this.$activeElements = { + $button : this.$button + } + } + + return this.$activeElements; + } + +}).call(apf.dropdown.prototype = new apf.BaseList()); + +apf.config.$inheritProperties["initial-message"] = 1; + +apf.aml.setElement("dropdown", apf.dropdown); + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/elements/editor.js)SIZE(18601)TIME(Fri, 14 Oct 2011 08:01:44 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/elements/errorbox.js)SIZE(6106)TIME(Fri, 14 Oct 2011 08:01:44 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + + +/** + * Element showing an error message when the attached element + * is in erroneous state and has the invalidmsg="" attribute specified. + * In most cases the errorbox element is implicit and will be created + * automatically. + * Example: + * + * + * Invalid e-mail address entered. + * + * + * Remarks: + * In most cases the errorbox element is not created directly but implicitly + * by a validationgroup. An element that goes into an error state will + * show the errorbox. + * + * + * Phone number + * + * + * Password + * + * Validate + * + * + * + * To check if the element has valid information entered, leaving the textbox + * (focussing another element) will trigger a check. Programmatically a check + * can be done using the following code: + * + * txtPhone.validate(); + * + * //Or use the html5 syntax + * txtPhone.checkValidity(); + * + * + * To check for the entire group of elements use the validation group. For only + * the first non-valid element the errorbox is shown. That element also receives + * focus. + * + * vgForm.validate(); + * + * + * @constructor + * @define errorbox + * + * @allowchild {anyxhtml} + * @addnode elements + * + * @inherits apf.Presentation + * + * @author Ruben Daniels (ruben AT ajax DOT org) + * @version %I%, %G% + * @since 0.4 + */ +apf.errorbox = function(struct, tagName){ + this.$init(tagName || "errorbox", apf.NODE_VISIBLE, struct); +}; + +(function(){ + this.$positioning = "basic"; + this.display = function(host){ + this.host = host; + + var refHtml = + + host.validityState && host.validityState.$errorHtml || + + host.$ext; + + document.body.appendChild(this.$ext); + /*var pos = apf.getAbsolutePosition(refHtml, document.body); + + if (document != refHtml.ownerDocument) { + var pos2 = apf.getAbsolutePosition(refHtml.ownerDocument.parentWindow.frameElement, document.body); + pos[0] += pos2[0]; + pos[1] += pos2[1]; + }*/ + + var x = (parseFloat(host.$getOption && host.$getOption("main", "erroffsetx") || 0)), + y = (parseFloat(host.$getOption && host.$getOption("main", "erroffsety") || 0)); + //this.$ext.style.left = x + "px" + //this.$ext.style.top = y + "px" + + this.show(); + apf.popup.show(this.$uniqueId, { + x : x, + y : y, + animate : false, + ref : refHtml, + allowTogether: true + }); + + this.$setStyleClass(this.$ext, + x + this.$ext.offsetWidth > this.$ext.offsetParent.offsetWidth + ? "rightbox" + : "leftbox", ["leftbox", "rightbox"]); + }; + + /** + * Sets the message of the errorbox. + * @param {String} value + */ + this.setMessage = function(value){ + if (value && value.indexOf(";") > -1) { + value = value.split(";"); + value = "" + value.shift() + "" + value.join(";"); + } + this.$int.innerHTML = value || ""; + }; + + this.$draw = function(){ + //Build Main Skin + this.$ext = this.$getExternal(); + this.$int = this.$getLayoutNode("main", "container", this.$ext); + this.oClose = this.$getLayoutNode("main", "close", this.$ext); + + if (this.oClose) { + var _self = this; + this.oClose.onclick = function(){ + _self.hide(); + + if (apf.document.activeElement) + apf.document.activeElement.focus(true, {mouse:true}); + }; + } + + this.$ext.onmousedown = function(e){ + (e || event).cancelBubble = true; + + + if (apf.hasFocusBug) + apf.window.$focusfix(); + + } + + apf.popup.setContent(this.$uniqueId, this.$ext, "", null, null); + }; + + this.$loadAml = function(x){ + if (!apf.isTrue(this.getAttribute("visible"))) + this.hide(); + }; + + this.$destroy = function(){ + if (this.oClose) + this.oClose.onclick = null; + + this.$ext.onmousedown = null; + + apf.popup.removeContent(this.$uniqueId); + }; +}).call(apf.errorbox.prototype = new apf.Presentation()); + +apf.aml.setElement("errorbox", apf.errorbox); + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/elements/event.js)SIZE(2115)TIME(Fri, 14 Oct 2011 08:01:44 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + + +/** + * Displays a popup element with a message with optionally an icon at the + * position specified by the position attribute. After the timeout has passed + * the popup will dissapear automatically. When the mouse hovers over the popup + * it doesn't dissapear. + * + * @event click Fires when the user clicks on the representation of this event. + */ +apf.event = function(struct, tagName){ + this.$init(tagName || "event", apf.NODE_HIDDEN, struct); +}; + +(function() { + this.$hasInitedWhen = false; + + this.$booleanProperties["repeat"] = true; + this.$supportedProperties.push("when", "message", "icon", "repeat"); + + this.$propHandlers["when"] = function(value) { + if (this.$hasInitedWhen && value && this.parentNode && this.parentNode.popup) { + var _self = this; + $setTimeout(function() { + _self.parentNode.popup(_self.message, _self.icon, _self); + }); + } + this.$hasInitedWhen = true; + + if (this.repeat) + delete this.when; + }; + + this.$loadAml = function(x) {}; +}).call(apf.event.prototype = new apf.AmlElement()); + +apf.aml.setElement("event", apf.event); + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/elements/filler.js)SIZE(1385)TIME(Fri, 14 Oct 2011 08:01:44 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + +apf.filler = function(struct, tagName){ + this.$init(tagName || "filler", apf.NODE_VISIBLE, struct); +}; + +(function() { + this.$focussable = false; + this.flex = 1; + + this.$draw = function() { + this.$ext = this.$pHtmlNode.appendChild(this.$pHtmlNode.ownerDocument.createElement("div")); + }; +}).call(apf.filler.prototype = new apf.GuiElement()); + +apf.aml.setElement("filler", apf.filler); + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/elements/flashplayer.js)SIZE(5856)TIME(Wed, 30 Nov 2011 17:29:20 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + +/** + * Element displaying the contents of a .swf (adobe flash) file. + * + * @constructor + * @define flashplayer + * @allowchild {smartbinding} + * @addnode elements + * + * @inherits apf.BaseSimple + * + * @author Ruben Daniels (ruben AT ajax DOT org) + * @version %I%, %G% + * @since 0.9 + * + * @binding value Determines the way the value for the element is retrieved + * from the bound data. + * Example: + * Sets the flash source text based on data loaded into this component. + * + * + * + * + * + * + * + */ +apf.flashplayer = function(struct, tagName){ + this.$init(tagName || "flashplayer", apf.NODE_VISIBLE, struct); +}; + +(function(){ + this.implement( + + apf.DataAction, + + apf.StandardBinding + ); + + /**** Public methods ****/ + + + + /** + * @ref global#setValue + */ + this.setSrc = function(value){ + this.setProperty("src", value, false, true); + }; + + + + /**** Properties and attributes ****/ + + this.$supportedProperties.push("value", "allowfullscreen", "flashvars"); + this.$propHandlers["src"] = function(value){ + //this.$ext.getElementsByTagName("param")[2].setAttribute("value", value); + this.$ext.getElementsByTagName("object")[0].setAttribute("data", value); +// this.$ext.getElementsByTagName("embed")[0].setAttribute("src", value); + }; + this.$propHandlers["allowfullscreen"] = function(value){ + this.$ext.getElementsByTagName("param")[1].setAttribute("value", value); +// this.$ext.getElementsByTagName("embed")[0].setAttribute("allowFullScreen", value); + } + this.$propHandlers["flashvars"] = function(value){ + this.$ext.getElementsByTagName("param")[3].setAttribute("value", value); +// this.$ext.getElementsByTagName("embed")[0].setAttribute("flashvars", value); + } + this.$propHandlers["bgcolor"] = function(value){ + this.$ext.getElementsByTagName("param")[4].setAttribute("value", value); +// this.$ext.getElementsByTagName("embed")[0].setAttribute("bgcolor", value); + } + this.$propHandlers["wmode"] = function(value){ + this.$ext.getElementsByTagName("param")[5].setAttribute("value", value); +// this.$ext.getElementsByTagName("embed")[0].setAttribute("wmode", value); + } + + /**** Init ****/ + + this.$draw = function(){ + //Build Main Skin + this.$ext = this.$pHtmlNode.appendChild(document.createElement("div")); + if (this.getAttribute("style")) + this.$ext.setAttribute("style", this.getAttribute("style")); + this.$ext.onclick = function(){this.host.dispatchEvent("click");} + + var src = this.getAttribute("src") || ""; + this.$ext.insertAdjacentHTML("beforeend", + '\ + \ + \ + \ + \ + \ + \ + ') + /* \ + \ + \ + \ + \ + \ + \ + \ + \ +'\ + \*/ + }; + + this.$loadAml = function(x){ + }; +}).call(apf.flashplayer.prototype = new apf.GuiElement()); + +apf.aml.setElement("flashplayer", apf.flashplayer); + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/elements/flowchart.js)SIZE(50799)TIME(Fri, 14 Oct 2011 08:01:44 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/elements/flowchart2.js)SIZE(45889)TIME(Fri, 14 Oct 2011 08:01:44 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/elements/frame.js)SIZE(4838)TIME(Tue, 06 Dec 2011 16:22:54 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + + +/** + * Element displaying a frame with a caption, containing other elements. This + * element is called a fieldset in html. + * Example: + * + * + * Option 1 + * Option 2 + * Option 3 + * Option 4 + * + * + * + * @constructor + * @define fieldset, frame + * @allowchild {elements}, {anyaml} + * @addnode elements:frame + * + * @author Ruben Daniels (ruben AT ajax DOT org) + * @version %I%, %G% + * @since 0.9 + * + * @inherits apf.Presentation + */ +apf.panel = function(struct, tagName){ + this.$init(tagName || "panel", apf.NODE_VISIBLE, struct); +}; + +apf.fieldset = function(struct, tagName){ + this.$init(tagName || "fieldset", apf.NODE_VISIBLE, struct); +}; + +apf.frame = function(struct, tagName){ + this.$init(tagName || "submit", apf.NODE_VISIBLE, struct); +}; + +(function(){ + this.implement(apf.BaseStateButtons); + + this.$focussable = false; + + + + /**** Properties and Attributes ****/ + + /** + * @attribute {String} caption the text of the caption. + */ + this.$supportedProperties.push("caption", "url"); + this.$propHandlers["caption"] = function(value){ + if (!this.oCaption) return; + + if (this.oCaption.nodeType == 1) + this.oCaption.innerHTML = value; + else + this.oCaption.nodeValue = value; + }; + + /** + * @attribute {String} icon the location of the image. + */ + this.$propHandlers["icon"] = function(value){ + var oIcon = this.$getLayoutNode("main", "icon", this.$ext); + if (!oIcon) return; + + if (oIcon.nodeType == 1) + oIcon.style.display = value ? "block" : "none"; + apf.skins.setIcon(oIcon, value, this.iconPath); + }; + + this.$propHandlers["url"] = function(value){ + var node = this.oCaption; + if (node.tagName == "A" || node.nodeType != 1) + node = node.parentNode; + + node.innerHTML = "" + + this.caption + ""; + this.oCaption = this.oCaption.firstChild; + }; + + /** + * Sets the text of the title of this element + * @param {String} value the text of the title. + */ + this.setTitle = function(value){ + this.setProperty("title", value); + }; + + /**** Init ****/ + + this.$draw = function(){ + //Build Main Skin + this.$ext = this.$getExternal(null, null, function(oExt){ + this.$initButtons(oExt); + }); + this.oCaption = this.$getLayoutNode("main", "caption", this.$ext); + this.$int = this.$getLayoutNode("main", "container", this.$ext); + this.$buttons = this.$getLayoutNode("main", "buttons", this.$ext); + + /*if (this.oCaption) { + this.oCaption = this.oCaption.nodeType == 1 + ? this.oCaption + : this.oCaption.parentNode; + }*/ + }; + + this.$loadAml = function(x){ + // not implement now. + }; + + + this.$getActiveElements = function() { + // init $activeElements + if (!this.$activeElements) { + this.$activeElements = { + oCaption : this.oCaption + } + } + + return this.$activeElements; + } + +}).call(apf.frame.prototype = new apf.Presentation()); + +apf.panel.prototype = +apf.fieldset.prototype = apf.frame.prototype; + +apf.aml.setElement("panel", apf.panel); +apf.aml.setElement("fieldset", apf.fieldset); +apf.aml.setElement("frame", apf.frame); + + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/elements/gallery.js)SIZE(27418)TIME(Fri, 14 Oct 2011 08:01:44 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/elements/graph.js)SIZE(21525)TIME(Fri, 14 Oct 2011 08:01:44 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/elements/hbox.js)SIZE(41468)TIME(Mon, 19 Dec 2011 11:16:16 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + +/** + * @define vbox Container that stacks it's children vertically. + * @see element.hbox + * @define hbox Container that stacks it's children horizontally. + * Example: + * + * + * + * + * + * + * + * + * + * + * + * + * Remarks: + * Firefox has some issues. + * 1. Sometimes it's necessary to put a fixed width to have it calculate the right + * height value. + * 2. Using flex="1" on non fixed height/width tree's will give unexpected results. + * @addnode elements + * @constructor + * + * @author Ruben Daniels (ruben AT ajax DOT org) + * @version %I%, %G% + * @since 0.9 + */ +apf.hbox = function(struct, tagName){ + this.$init(tagName || "hbox", apf.NODE_VISIBLE, struct); +}; +apf.vbox = function(struct, tagName){ + this.$init(tagName || "vbox", apf.NODE_VISIBLE, struct); +}; + +(function(){ + /**** Properties and Attributes ****/ + + this.$focussable = false; + this.$useLateDom = true; + this.$box = true; + this.$layout = true; + + var MOZSTACK = "-moz-stack"; + var input = {"INPUT":1, "SELECT":1, "TEXTAREA":1} + + /** + * @attribute {String} padding the space between each element. Defaults to 2. + * @attribute {Boolean} reverse whether the sequence of the elements is in reverse order. + * @attribute {String} edge the space between the container and the elements, space seperated in pixels for each side. Similar to css in the sequence top right bottom left. Defaults to "5 5 5 5". + * Example: + * + * + * + * @attribute {String} pack + * Possible values: + * start + * center + * end + * @attribute {Boolean} align + * Possible values: + * start + * center + * end + * stretch + */ + this.$booleanProperties["splitters"] = true; + this.$supportedProperties.push("padding", "reverse", "edge", "pack", "align", "splitters"); + + this.$propHandlers["padding"] = function(value){ + this.padding = parseInt(value); + + var node, nodes = this.childNodes, elms = []; + for (var i = 0, l = nodes.length; i < l; i++) { + if ((node = nodes[i]).nodeFunc == apf.NODE_VISIBLE && node.$amlLoaded && node.visible !== false) + elms.push(node); + } + + if (!elms.length) + return; + + for (var last, b, el, i = elms.length - 2; i >= 0; i--) { + b = (el = elms[i]).margin && apf.getBox(el.margin) || [0,0,0,0]; + + if ((!last || !last.$splitter) && !el.$splitter) { + b[this.$vbox ? 2 : 1] += this.padding; + + if (!apf.hasFlexibleBox && i != 0 && this.align == "stretch" && this.$vbox) + b[0] += this.padding; + } + + el.$ext.style.margin = b.join("px ") + "px"; + last = el; + } + b = (el = elms[elms.length - 1]).margin && apf.getBox(el.margin) || [0,0,0,0]; + el.$ext.style.margin = b.join("px ") + "px"; + + if (!apf.hasFlexibleBox) + this.$resize(); + } + + this.$propHandlers["reverse"] = function(value){ + if (apf.hasFlexibleBox) + this.$int.style[apf.CSSPREFIX + "BoxDirection"] = value ? "reverse" : "normal"; + else { + //@todo + } + }; + + this.$propHandlers["edge"] = function(value){ + var el = !apf.hasFlexibleBox && this.$vbox ? this.$ext : this.$int; + el.style.padding = (this.$edge = apf.getBox(value)).join("px ") + "px"; + + if (!apf.hasFlexibleBox) + this.$resize(); + }; + + this.$propHandlers["pack"] = function(value){ + if (apf.hasFlexibleBox) + this.$int.style[apf.CSSPREFIX + "BoxPack"] = value || "start"; + else if (this.$amlLoaded) { + if (this.$vbox) { + this.$int.style.verticalAlign = value == "center" ? "middle" : (value == "end" ? "bottom" : "top"); + } + else { + this.$int.style.textAlign = ""; + + var nodes = this.childNodes; + for (var i = 0, l = nodes.length; i < l; i++) { + if ((node = nodes[i]).nodeFunc != apf.NODE_VISIBLE || !node.$amlLoaded) //|| node.visible === false + continue; + + node.$ext.style.textAlign = apf.getStyle(node.$ext, "textAlign") || "left"; + } + + this.$int.style.textAlign = value == "center" ? "center" : (value == "end" ? "right" : "left"); + } + } + }; + + //@todo change overflow when height/width changes depending on $vbox + + this.$propHandlers["align"] = function(value){ + if (apf.hasFlexibleBox) { + this.$int.style[apf.CSSPREFIX + "BoxAlign"] = value || "stretch"; + + if (apf.isGecko) + this.$int.style.overflow = "visible"; + + //@todo this should probably be reinstated + var stretch = !value || value == "stretch"; + var nodes = this.childNodes; + var size = this.$vbox ? "width" : "height"; + + var isInFixed = false, loopNode = this; + while(!isInFixed && loopNode) { + isInFixed = loopNode[size] || loopNode.anchors || (loopNode.$vbox ? loopNode.top && loopNode.bottom : loopNode.left && loopNode.right); + if (!loopNode.flex) + break; + loopNode = loopNode.parentNode || loopNode.$parentNode; + } + + for (var i = 0, l = nodes.length; i < l; i++) { + if (!(node = nodes[i]).$ext || node.$ext.nodeType != 1) + continue; + + //(this[size] || this.anchors || (this.$vbox ? this.top && this.bottom : this.left && this.right) + if (stretch && !node[size]) //(node.$altExt || + node.$ext.style[size] = apf.isGecko && (this.flex || node.flex) + ? (isInFixed ? "1px" : "auto") + : (apf.isWebkit && input[node.$ext.tagName] + ? "100%" + : (apf.isWebkit && node[this.$vbox ? "minwidth" : "minheight"] && this.flex //nasty bug fix + ? "0px" + : "auto"));//(apf.isWebkit && node.flex && size == "height" ? "100%" : "auto"); // && (this.flex && node.flex) + else if (node[size]) + handlers["true"][size].call(node, node[size]); + } + } + else if (this.$amlLoaded) { + var stretch = !value || value == "stretch"; + + if (!this.$vbox) { + var nodes = this.childNodes; + for (var i = 0, l = nodes.length; i < l; i++) { + if ((node = nodes[i]).nodeFunc != apf.NODE_VISIBLE || !node.$amlLoaded) //|| node.visible === false + continue; + + node.$ext.style.verticalAlign = value == "center" ? "middle" : (value == "end" ? "bottom" : "top"); + } + } + else { + var el = !apf.hasFlexibleBox && this.$vbox ? this.$ext : this.$int; + el.style.textAlign = ""; + + var node, nodes = this.childNodes; + for (var i = 0, l = nodes.length; i < l; i++) { + if ((node = nodes[i]).nodeFunc != apf.NODE_VISIBLE || !node.$amlLoaded) //|| node.visible === false + continue; + + if (node.visible !== false) { + node.$ext.style.display = value == "stretch" ? "block" : apf.INLINE; + node.$br.style.display = value == "stretch" ? "none" : ""; + + if (apf.needZoomForLayout) + node.$ext.style.zoom = 1; + } + node.$ext.style.textAlign = apf.getStyle(node.$ext, "textAlign") || "left"; + } + + el.style.textAlign = value == "center" ? "center" : (value == "end" ? "right" : "left"); + } + } + }; + + function visibleHandler(e){ + + if (this.parentNode.splitters && !this.$splitter) { + if (!e.value) { + if (this.nextSibling && this.nextSibling.$splitter) + this.nextSibling.removeNode(); + else if (this.previousSibling && this.previousSibling.$splitter) + this.previousSibling.removeNode(); + } + else { + var isLast = isLastVisibleChild(this); + if (!isLast) { + if (!this.nextSibling.$splitter && !this.nextSibling.nosplitter + && !isFirstVisibleChild(this) && !this.nosplitter) { + this.parentNode.insertBefore( + this.ownerDocument.createElementNS(apf.ns.aml, "splitter"), + this.nextSibling); + } + } + else if (this.previousSibling && !this.previousSibling.$splitter + && !this.previousSibling.nosplitter) { + this.parentNode.insertBefore( + this.ownerDocument.createElementNS(apf.ns.aml, "splitter"), + this); + } + } + } + + + //@todo this can be more optimized by calcing if it WAS the last vis child. + if (this.parentNode.$propHandlers["padding"]) {// && isLastVisibleChild(this)) { + this.parentNode.$propHandlers["padding"] + .call(this.parentNode, this.parentNode.padding); + } + + apf.layout.forceResize(this.parentNode.$int); + + if (apf.hasFlexibleBox) { + if (this.$altExt) + this.$altExt.style.display = e.value + ? (apf.isGecko ? MOZSTACK : apf.CSSPREFIX2 + "-box") + : "none"; + return; + } + + if (e.value) { + this.$ext.style.display = this.parentNode.$vbox + && this.parentNode.align == "stretch" ? "block" : apf.INLINE; + if (apf.needZoomForLayout) + this.$ext.style.zoom = 1; + if (this.$br) + this.$br.style.display = this.parentNode.align == "stretch" ? "none" : ""; + } + else { + if (this.$br) + this.$br.style.display = "none"; + } + + this.parentNode.$resize(); + } + + function resizeHandler(){ + if (!this.flex) { + if (this.$isRszHandling || this.$lastSizeChild && + this.$lastSizeChild[0] == this.$ext.offsetWidth && + this.$lastSizeChild[1] == this.$ext.offsetHeight) + return; + + /*if (this.$skipResizeOnce) + delete this.$skipResizeOnce; + else*/ + this.parentNode.$resize(true); + + this.$lastSizeChild = [this.$ext.offsetWidth, this.$ext.offsetHeight]; + } + } + + var handlers = { + //Handlers for flexible box layout + "true" : { + "optimize" : function(value){ + this.optimize = apf.isTrue(value); + }, + + "width" : function(value){ + //@todo this should check the largest and only allow that one + //if (this.parentNode.$vbox && this.parentNode.align == "stretch") + //return; + + (this.$altExt || this.$ext).style.width = value + ? (parseFloat(value) == value + ? value + "px" + : value) + : ""; + }, + + "height" : function(value){ + //@todo this should check the largest and only allow that one + //if (!this.parentNode.$vbox && this.parentNode.align == "stretch") + //return; + + (this.$altExt || this.$ext).style.height = value + ? (parseFloat(value) == value + ? value + "px" + : value) + : (apf.isGecko && this.flex && this.parentNode.$vbox ? "auto" : ""); + }, + + "margin" : function(value){ + var b = apf.getBox(value); + if (!isLastVisibleChild(this)) + b[this.parentNode.$vbox ? 2 : 1] += this.parentNode.padding; + this.$ext.style.margin = b.join("px ") + "px"; + }, + + "flex" : function(value){ + this.flex = value = parseInt(value); + if (value) { + if (!this.optimize && !this.$altExt) { + this.$altExt = this.$ext.ownerDocument.createElement("div"); + this.parentNode.$int.replaceChild(this.$altExt, this.$ext); + this.$altExt.appendChild(this.$ext); + this.$altExt.style[apf.CSSPREFIX + "BoxSizing"] = "border-box"; + this.$altExt.style.display = apf.CSSPREFIX2 + "-box"; + this.$altExt.style[apf.CSSPREFIX + "BoxOrient"] = "vertical"; + this.$ext.style[apf.CSSPREFIX + "BoxFlex"] = 1; + var size = this.parentNode.$vbox ? "height" : "width"; + //var osize = this.parentNode.$vbox ? "width" : "height"; + + if (apf.isWebkit) { + if (!this.preventforcezero) + this.$altExt.style[size] = "0px"; + } + else if (apf.isGecko) { + this.$altExt.style[size] = "0px"; + + //Possible hack to not do this for $box elements + if (!this.$box) + this.$altExt.style.overflow = "hidden"; //Gecko + if (apf.getStyle(this.$ext, "overflow") == "visible") + this.$ext.style.overflow = "hidden"; //Gecko + this.$ext.style[size] = "1px"; + + this.$altExt.style.minHeight = this.$ext.style.minHeight; + this.$altExt.style.maxHeight = this.$ext.style.maxHeight; + this.$altExt.style.minWidth = this.$ext.style.minWidth; + this.$altExt.style.maxWidth = this.$ext.style.maxWidth; + } + } + + (this.$altExt || this.$ext).style[apf.CSSPREFIX + "BoxFlex"] = parseInt(value) || 1; + } + else if (this.$altExt) { + this.parentNode.$int.replaceChild(this.$ext, this.$altExt); + this.$ext.style[apf.CSSPREFIX + "BoxFlex"] = ""; + if (apf.isGecko) + this.$ext.style.overflow = ""; + delete this.$altExt; + } + } + }, + + //Handlers for older browsers + "false" : { + "width" : function(value){ + //@todo this should check the largest and only allow that one + //if (this.parentNode.$vbox && this.parentNode.align == "stretch") + //return; + + this.$ext.style.width = value + ? (parseFloat(value) == value + ? Math.max(0, value - apf.getWidthDiff(this.$ext)) + "px" + : value) + : ""; + }, + + "height" : function(value){ + //@todo this should check the largest and only allow that one + //if (this.parentNode.localName == "hbox" && this.parentNode.align == "stretch") + //return; + + this.$ext.style.height = value + ? (parseFloat(value) == value + ? Math.max(0, value - apf.getHeightDiff(this.$ext)) + "px" + : value) + : ""; + }, + + "margin" : function(value){ + var b = apf.getBox(value); + if (this.padding) { + if (!isLastVisibleChild(this)) + b[this.parentNode.$vbox ? 2 : 1] += this.padding; + if (this != this.parentNode.firstChild && this.parentNode.align == "stretch" && this.parentNode.$vbox) //@todo + b[0] += this.padding; + } + this.$ext.style.margin = b.join("px ") + "px"; + }, + + "flex" : function(value){ + this.flex = parseInt(value); + if (this.$amlLoaded) + this.parentNode.$resize(true); + } + } + } + + function isFirstVisibleChild(amlNode){ + var firstChild = amlNode.parentNode.firstChild; + while (firstChild && (firstChild.nodeFunc != apf.NODE_VISIBLE + || firstChild.visible === false + || firstChild.visible == 2 && apf.isFalse(firstChild.getAttribute("visible")))) { + firstChild = firstChild.nextSibling; + } + + return firstChild && firstChild == amlNode; + } + + function isLastVisibleChild(amlNode){ + var lastChild = amlNode.parentNode.lastChild; + while (lastChild && (lastChild.nodeFunc != apf.NODE_VISIBLE + || lastChild.visible === false + || lastChild.visible == 2 && apf.isFalse(lastChild.getAttribute("visible")))) { + lastChild = lastChild.previousSibling; + } + + return lastChild && lastChild == amlNode; + } + + //@todo move this to enableTable, disableTable + this.register = function(amlNode, insert){ + if (amlNode.$altExt) //@todo hack, need to re-arch layouting + return; + + amlNode.$propHandlers["left"] = + amlNode.$propHandlers["top"] = + amlNode.$propHandlers["right"] = + amlNode.$propHandlers["bottom"] = apf.K; + + var propHandlers = handlers[apf.hasFlexibleBox]; + for (var prop in propHandlers) { + amlNode.$propHandlers[prop] = propHandlers[prop]; + } + + if (amlNode.nodeFunc == apf.NODE_VISIBLE) { + if (apf.hasFlexibleBox) { + //if (apf.isGecko && apf.getStyle(amlNode.$ext, "display") == "block") + //amlNode.$ext.style.display = MOZSTACK; //@todo visible toggle + + //input elements are not handled correctly by firefox and webkit + if (amlNode.$ext.tagName == "INPUT" || apf.isWebkit && input[amlNode.$ext.tagName]) { + var doc = amlNode.$ext.ownerDocument; + amlNode.$altExt = doc.createElement("div"); + amlNode.parentNode.$int.replaceChild(amlNode.$altExt, amlNode.$ext); + amlNode.$altExt.style[apf.CSSPREFIX + "BoxSizing"] = "border-box"; + amlNode.$altExt.appendChild(amlNode.$ext); + + if (apf.isWebkit) { + var d = apf.getDiff(amlNode.$ext); + //amlNode.$altExt.style.padding = "0 " + d[0] + "px " + d[1] + "px 0"; + amlNode.$altExt.style.height = "100%"; + amlNode.$altExt.style.width = "0"; + amlNode.$altExt.style.lineHeight = 0; + amlNode.$altExt.style.margin = "-1px 0 0 0"; + amlNode.$ext.style.width = "100%"; + amlNode.$ext.style.height = "100%"; + amlNode.$ext.style.top = "1px"; + amlNode.$ext.style.position = "relative"; + } + else { + amlNode.$altExt.style.display = apf.CSSPREFIX2 + "-box"; + amlNode.$altExt.style[apf.CSSPREFIX + "BoxOrient"] = "horizontal"; + amlNode.$altExt.style[apf.CSSPREFIX + "BoxAlign"] = "stretch"; + amlNode.$ext.style[apf.CSSPREFIX + "BoxFlex"] = 1; + } + } + else { + if (apf.getStyle(amlNode.$ext, "display") == "inline") + amlNode.$ext.style.display = "block"; //@todo undo + //This is nice for positioning elements in the context of an hbox/vbox + //if (apf.getStyle(amlNode.$ext, "position") == "absolute") + //amlNode.$ext.style.position = "relative"; //@todo undo + } + + amlNode.$ext.style[apf.CSSPREFIX + "BoxSizing"] = "border-box"; + } + else { + if (this.$vbox) { + amlNode.$br = this.$int.insertBefore(amlNode.$ext.ownerDocument.createElement("br"), amlNode.$ext.nextSibling); + if (amlNode.visible === false) + amlNode.$br.style.display = "none"; + } + else { + if (amlNode.visible !== false) { + amlNode.$ext.style.display = apf.INLINE; + if (apf.needZoomForLayout) + amlNode.$ext.style.zoom = 1; + } + this.$int.style.whiteSpace = ""; + amlNode.$ext.style.whiteSpace = apf.getStyle(amlNode.$ext, "whiteSpace") || "normal"; + this.$int.style.whiteSpace = "nowrap"; + } + + this.$int.style.fontSize = "0"; + if (!amlNode.$box) { + var fontSize = apf.getStyle(amlNode.$ext, "fontSize"); + if (fontSize == "0px") { + amlNode.$ext.style.fontSize = ""; + var pNode = this.$int.parentNode; + while(apf.getStyle(pNode, "fontSize") == "0px") { + pNode = pNode.parentNode; + } + fontSize = apf.getStyle(pNode, "fontSize"); + } + amlNode.$ext.style.fontSize = fontSize;//apf.getStyle(amlNode.$ext, "fontSize") || "normal"; + } + + amlNode.addEventListener("resize", resizeHandler); + } + + amlNode.addEventListener("prop.visible", visibleHandler); + + this.$noResize = true; + + if (amlNode.height) + propHandlers.height.call(amlNode, amlNode.height); + if (amlNode.width) + propHandlers.width.call(amlNode, amlNode.width); + if (amlNode.margin) + propHandlers.margin.call(amlNode, amlNode.margin); + if (amlNode.flex) + propHandlers.flex.call(amlNode, amlNode.flex); + + //Ie somehow sets the visible flags in between registration + var isLast = isLastVisibleChild(amlNode); //apf.isIE ? this.lastChild == amlNode : + if (isLast || insert) { + this.$propHandlers["padding"].call(this, this.padding); + this.$propHandlers["align"].call(this, this.align); + + if (!apf.hasFlexibleBox) + this.$propHandlers["pack"].call(this, this.pack); + + if (amlNode.visible !== false) //insert && - removed because for new nodes that are being attached to the tree insert is not set + visibleHandler.call(amlNode, {value: true}); + + //@todo this needs more work + if (insert && amlNode.previousSibling) { + var prev = amlNode.previousSibling; + while (prev && (prev.nodeType != 1 || prev.localName == "splitter")) + prev = prev.previousSibling; + if (prev) + visibleHandler.call(prev, {value: true}); + } + } + + else if (this.splitters && !amlNode.$splitter && amlNode.visible !== false && !amlNode.nosplitter) { + if (amlNode.$ext.nextSibling != (amlNode.nextSibling + && (amlNode.nextSibling.$altExt || amlNode.nextSibling.$ext))) { + var _self = this; + setTimeout(function(){ + _self.insertBefore( + _self.ownerDocument.createElementNS(apf.ns.aml, "splitter"), + amlNode.nextSibling); + }); + } + else { + this.insertBefore( + this.ownerDocument.createElementNS(apf.ns.aml, "splitter"), + amlNode.nextSibling); + } + } + + + delete this.$noResize; + + if (!apf.hasFlexibleBox && isLast) + this.$resize(); + } + } + + this.unregister = function(amlNode){ + if(!amlNode.$propHandlers) + return; + + amlNode.$propHandlers["left"] = + amlNode.$propHandlers["top"] = + amlNode.$propHandlers["right"] = + amlNode.$propHandlers["bottom"] = null; + + var propHandlers = handlers[apf.hasFlexibleBox]; + for (var prop in propHandlers) { + delete amlNode.$propHandlers[prop]; + } + + //Clear css properties and set layout + if (amlNode.nodeFunc == apf.NODE_VISIBLE) { + if (amlNode.flex) { + var flex = amlNode.flex; + propHandlers.flex.call(amlNode, 0); + amlNode.flex = flex; + } + + if (apf.hasFlexibleBox) { + amlNode.$ext.style[apf.CSSPREFIX + "BoxSizing"] = ""; + + if (apf.isGecko) { + this.$int.style.overflow = "visible"; + + if (amlNode.$ext.style.display == "block") + amlNode.$ext.style.display = ""; + } + } + else { + amlNode.$ext.style.verticalAlign = ""; + amlNode.$ext.style.textAlign = ""; + amlNode.$ext.style.whiteSpace = ""; + //amlNode.$ext.style[apf.CSSFLOAT] = ""; + + if (amlNode.$br) { + amlNode.$br.parentNode.removeChild(amlNode.$br); + delete amlNode.$br; + //amlNode.$ext.style.fontSize = ""; + } + + amlNode.removeEventListener("resize", resizeHandler); + } + + amlNode.removeEventListener("prop.visible", visibleHandler); + + amlNode.$ext.style.display = amlNode.visible ? "block" : "none"; + + if (amlNode.margin) + amlNode.$ext.style.margin = ""; + + if (amlNode.width) + amlNode.$ext.style.width = ""; + + + if (this.splitters && !amlNode.$splitter) { + if (amlNode.nextSibling && amlNode.nextSibling.$splitter) + amlNode.nextSibling.removeNode(); + if (isLastVisibleChild(amlNode) && amlNode.previousSibling + && amlNode.previousSibling.$splitter) + amlNode.previousSibling.removeNode(); + } + + } + } + /* + this.addEventListener("DOMNodeInsertedIntoDocument", function(e){ + this.register(this.parentNode); + }); + */ + + /**** DOM Hooks ****/ + + this.addEventListener("DOMNodeRemoved", function(e){ + if (e.$doOnlyAdmin || e.currentTarget == this) + return; + + if (e.relatedNode == this){ + this.unregister(e.currentTarget); + //e.currentTarget.$setLayout(); + } + }); + + this.addEventListener("DOMNodeInserted", function(e){ + if (e.currentTarget == this) { + if (this.visible) + this.$ext.style.display = apf.CSSPREFIX2 + "-box"; //Webkit issue + return; + } + + if (e.currentTarget.nodeType != 1) + return; + + if (e.relatedNode == this && !e.$isMoveWithinParent) { + e.currentTarget.$setLayout(this.localName, true); + + if (e.currentTarget.$altExt) { + + return false; + } + } + }); + + function myVisibleHandler(e){ + if (e.value) + this.$int.style.display = apf.CSSPREFIX2 + "-box"; + } + + function myHeightHandler(e){ + clearInterval(this.$heighttimer); + if (e.value || this.align != "stretch") { + delete this.$heighttimer; + } + else if (!this.$heighttimer) { + var _self = this; + this.$heighttimer = $setInterval(function(){ + if (_self.$amlDestroyed) + return; + + var nodes = _self.childNodes; + for (var $int, i = 0, l = nodes.length; i < l; i++) { + if (!($int = (node = nodes[i]).$int || node.$container)) + continue; + + if (Math.min($int.scrollHeight, node["maxheight"] || 10000) > $int.offsetHeight) + return _self.$resize(true); + } + + if (_self.flex) + clearInterval(this.$heighttimer); + }, this.flex ? 1 : 500); + } + } + + this.$draw = function(){ + var doc = this.$pHtmlNode.ownerDocument; + this.$ext = this.$pHtmlNode.appendChild(doc.createElement("div")); + if (this.getAttribute("style")) + this.$ext.setAttribute("style", this.getAttribute("style")); + this.$ext.className = this.localName; + + this.$vbox = this.localName == "vbox"; + this.$int = apf.isGecko && !this.parentNode.$box || !apf.hasFlexibleBox && this.$vbox //@todo reparenting for gecko needs some admin work + ? this.$ext.appendChild(doc.createElement("div")) + : this.$ext; + this.$ext.host = this; + + if (apf.isGecko && !this.parentNode.$box) { + this.$int.style.width = "100%"; + this.$int.style.height = "100%"; + } + else if (!apf.hasFlexibleBox && this.$vbox) { + this.$int.style.display = apf.INLINE; + if (apf.needZoomForLayout) + this.$int.style.zoom = 1; + this.$int.style.width = "100%"; + } + + if (apf.hasFlexibleBox) { + this.$display = "-" + apf.CSSPREFIX +"-box"; + + this.$int.style.display = apf.CSSPREFIX2 + "-box"; + this.$int.style[apf.CSSPREFIX + "BoxOrient"] = this.localName == "hbox" ? "horizontal" : "vertical"; + if (apf.isGecko) //!webkit + this.$int.style[apf.CSSPREFIX + "BoxSizing"] = "border-box"; + this.$int.style[apf.CSSPREFIX + "BoxAlign"] = "stretch"; + + this.addEventListener("prop.visible", myVisibleHandler); + } + else { + if (!this.$vbox) { + this.$int.style.whiteSpace = "nowrap"; + this.addEventListener("prop.height", myHeightHandler); + } + + var spacer = (!apf.hasFlexibleBox && this.$vbox ? this.$ext : this.$int) + .appendChild(doc.createElement("strong")); + spacer.style.height = "100%"; + spacer.style.display = apf.INLINE; + if (apf.needZoomForLayout) + spacer.style.zoom = 1; + //spacer.style.marginLeft = "-4px"; + spacer.style.verticalAlign = "middle"; + + this.addEventListener("resize", this.$resize); + } + + if (this.getAttribute("class")) + apf.setStyleClass(this.$ext, this.getAttribute("class")); + + this.$originalMin = [this.minwidth || 0, this.minheight || 0]; + }; + + this.$resize = function(force){ + if (!this.$amlLoaded || this.$noResize) // || apf.isIE7 && force !== true) + return; + + //Protection for stretch re-resizing + if (force !== true && this.$lastSize && + this.$lastSize[0] == this.$int.offsetWidth && + this.$lastSize[1] == this.$int.offsetHeight) + return; + + if (!apf.window.vManager.check(this, this.$uniqueId, this.$resize)) + return; +this.$noResize = true; + this.$lastSize = [this.$int.offsetWidth, this.$int.offsetHeight]; + + //this.$ext.style.border = "1px solid " + (["red", "green", "blue", "orange", "pink", "yellow"])[Math.round(Math.random() * 5)]; + + /*if (this.$table.offsetWidth >= this.$ext.offsetWidth) + this.$ext.style.minWidth = (this.minwidth = Math.max(0, this.$table.offsetWidth + - apf.getWidthDiff(this.$ext))) + "px"; + else { + this.$ext.style.minWidth = ""; + this.minwidth = this.$originalMin[0]; + } + + if (this.$table.offsetHeight >= this.$ext.offsetHeight) + this.$ext.style.minHeight = (this.minheight = Math.max(0, this.$table.offsetHeight + - apf.getHeightDiff(this.$ext))) + "px"; + else { + this.$ext.style.minHeight = ""; + this.minheight = this.$originalMin[1]; + }*/ + + //if (!this.$vbox) alert("here"); + + var total = 0; + var size = this.$vbox ? "width" : "height"; + var minsize = this.$vbox ? "minWidth" : "minHeight"; + var osize = this.$vbox ? "height" : "width"; + var scroll = this.$vbox ? "scrollWidth" : "scrollHeight"; + var offset = this.$vbox ? "offsetWidth" : "offsetHeight"; + var ooffset = this.$vbox ? "offsetHeight" : "offsetWidth"; + var getDiff = this.$vbox ? "getWidthDiff" : "getHeightDiff"; + var ogetDiff = this.$vbox ? "getHeightDiff" : "getWidthDiff"; + var inner = this.$vbox ? "getHtmlInnerWidth" : "getHtmlInnerHeight"; + var oinner = this.$vbox ? "getHtmlInnerHeight" : "getHtmlInnerWidth"; + var borders = this.$vbox ? "getVerBorders" : "getHorBorders"; + + var nodes = this.childNodes, hNodes = [], fW = 0, max = 0; + for (var node, i = 0; i < nodes.length; i++) { + if ((node = nodes[i]).nodeFunc != apf.NODE_VISIBLE || node.visible === false || !node.$amlLoaded) + continue; + + hNodes.push(node); + if (!node[size]) { + //if (!node.$skipResizeOnce) node.$skipResizeOnce = 1; + //else node.$skipResizeOnce++; + //node.$skipResizeOnce = 1 + //node.$ext.style[size] = ""; //@todo this is a sucky way of measuring + var m = node.margin && apf.getBox(node.margin); + if (m && this.$vbox) m.unshift(); + var mdiff = (m ? m[0] + m[2] : 0); + max = Math.max(max, mdiff + Math.min(node.$ext[scroll] + apf[borders](node.$ext), node["max" + size] || 10000)); + } + + if (parseInt(node.flex)) + total += parseFloat(node.flex); + else { + var m = node.margin && apf.getBox(node.margin); + if (m && !this.$vbox) m.shift(); + fW += node.$ext[ooffset] + (m ? m[0] + m[2] : 0); //this.padding + + } + } + if (!max && this[size]) { + max = this[size] + //- (this.$vbox ? this.$edge[0] + this.$edge[2] : this.$edge[1] + this.$edge[3]); + - apf[ogetDiff](this.$ext); + } + + /* + && (this[size] || this.flex) + */ + if (this.align == "stretch") { + //var hasSize = this[size] || this.flex; + var l = hNodes.length; + var pH = max;//this.$int[offset] - apf[getDiff](this.$int);// - (2 * this.padding); + for (var i = 0; i < l; i++) { + node = hNodes[i]; + + if (!node[size] && !this.$vbox || this.$vbox && input[node.$ext.tagName]) { + var m = node.margin && apf.getBox(node.margin); + if (m && this.$vbox) m.unshift(); + var mdiff = (m ? m[0] + m[2] : 0); + + /*shouldClear = !this[size] && !this.flex && node.$ext.offsetHeight == (pH - mdiff); + if (shouldClear) + node.$ext.style[size] = ""; + else + node.$ext.style[size] = Math.max(0, pH - apf[getDiff](node.$ext) - mdiff) + "px"; + node.$setResizeHeight = !shouldClear;*/ + + //!this[size] && !this.flex + if (max && Math.min(node.$ext[scroll], node["max" + size] || 10000) != max) + node.$ext.style[size] = Math.max(0, max - apf[getDiff](node.$ext) - mdiff) + "px"; + else + node.$ext.style[size] = ""; + + /*node.$ext.style[size] = !this[size] && !this.flex && node.$ext.offsetHeight == pH - mdiff + ? "" + : Math.max(0, pH - apf[getDiff](node.$ext) - mdiff) + "px";*/ + } + } + } + + //Flexing + if (total > 0) { + if (this.$vbox) + this.$int.style.height = "100%"; + this.$int.style.overflow = "hidden"; + + var splitterCount = apf.n(this).children("a:splitter").length() * 2; + + var rW = this.$int[ooffset] - apf[ogetDiff](this.$int) - fW + - ((hNodes.length - 1 - splitterCount) * this.padding);// - (2 * this.edge); + var lW = rW, done = 0; + for (var i = 0, l = hNodes.length; i < l; i++) { + if ((node = hNodes[i]).flex) { + var v = (i % 2 == 0 ? Math.floor : Math.ceil)((rW / total) * parseInt(node.flex)); + done += parseInt(node.flex); + var m = node.margin && apf.getBox(node.margin); + if (m && !this.$vbox) m.shift(); + node.$ext.style[osize] = Math.max(0, (done == total ? lW : v) - apf[ogetDiff](node.$ext) - (m ? m[0] + m[2] : 0)) + "px"; + lW -= v; + } + } + } + else { + if (this.$vbox) + this.$int.style.height = ""; + this.$int.style.overflow = ""; + } + + this.$noResize = false; + /*this.$noResize = true; + var _self = this; + setTimeout(function(){ + _self.$noResize = false; + });*/ + } + + this.$loadAml = function(x){ + if (this.padding == undefined) + this.padding = 0; + //this.$propHandlers.padding.call(this, this.padding = 0); + if (this.edge == undefined) + this.$propHandlers.edge.call(this, this.edge = 0); + if (this.pack == undefined) + this.$propHandlers.pack.call(this, this.edge = "start"); + if (this.align == undefined) + this.align = "stretch"; + //this.$propHandlers.align.call(this, this.align = "stretch"); + if (!apf.hasFlexibleBox && !this.$vbox && !this.height && this.align == "stretch") + myHeightHandler.call(this, {}); + }; +}).call(apf.vbox.prototype = new apf.GuiElement()); + +apf.hbox.prototype = apf.vbox.prototype; + +apf.aml.setElement("hbox", apf.hbox); +apf.aml.setElement("vbox", apf.vbox); + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/elements/iconmap.js)SIZE(3244)TIME(Fri, 14 Oct 2011 08:01:44 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/elements/img.js)SIZE(7692)TIME(Fri, 14 Oct 2011 08:01:44 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + + +/** + * Element displaying a picture. This element can read databound resources. + * Example: + * This example shows a list with pictures. When one is selected its displayed + * in the img element. + * + * + * + * + * + * + * + * + * + * + * + * path/to/image/[@src] + * + * + * + * + * + * @constructor + * @define img + * @allowchild {smartbinding} + * @addnode elements + * + * @inherits apf.BaseSimple + * + * @author Ruben Daniels (ruben AT ajax DOT org) + * @version %I%, %G% + * @since 0.4 + * + * @event click Fires when a user presses a mouse button while over this element. + * + * @binding value Determines the way the value for the element is retrieved + * from the bound data. + * Example: + * Sets the image source based on data loaded into this component. + * + * + * + * + * + * + */ +apf.img = function(struct, tagName){ + this.$init(tagName || "img", apf.NODE_VISIBLE, struct); +}; + +apf.preview = function(struct, tagName){ + this.$init(tagName || "preview", apf.NODE_VISIBLE, struct); +}; + +(function(){ + + + /** + * Sets the value of this element. This should be one of the values + * specified in the values attribute. + * @param {String} value the new value of this element + */ + this.setValue = function(value){ + this.setProperty("value", value, false, true); + }; + + /** + * Returns the current value of this element. + * @return {String} + */ + this.getValue = function(value){ + return this.value; + }; + + + + this.$supportedProperties.push("value", "src"); + /** + * @attribute {String} value the url location of the image displayed. + */ + this.$propHandlers["src"] = + this.$propHandlers["value"] = function(value){ + if (this.oImage.nodeType == 1) + this.oImage.style.backgroundImage = "url(" + value + ")"; + else + this.oImage.nodeValue = value; + + //@todo resize should become a generic thing + if (this.oImage.nodeType == 2 && !this.$resize.done) { + if (this.oImg) { + + //@todo add this to $destroy + var pNode = apf.hasSingleRszEvent ? this.$pHtmlNode : this.$ext; + apf.layout.setRules(pNode, this.$uniqueId + "_image", + "var o = apf.all[" + this.$uniqueId + "];\ + if (o) o.$resize()"); + apf.layout.queue(pNode); + + this.oImg.onload = function(){ + apf.layout.forceResize(pNode); + } + + } + + this.$resize.done = true; + } + + if (this.oImg) { + this.oImg.style.display = value ? "block" : "none"; + + //RLD: disabled lines below for the preview element. the image is probably not loaded yet. + //if (value) + //this.$resize(); + } + }; + + this.refetch = function(){ + this.$propHandlers["value"].call(this, "") + this.$propHandlers["value"].call(this, this.value || this.src) + } + + this.addEventListener("$clear", function(){ + this.value = ""; + + if (this.oImg) + this.oImg.style.display = "none"; + }); + + /**** Init ****/ + + this.$draw = function(){ + //Build Main Skin + this.$ext = this.$getExternal(); + this.$ext.onclick = function(e){ + this.host.dispatchEvent("click", {htmlEvent: e || event}); + }; + this.oImage = this.$getLayoutNode("main", "image", this.$ext); + if (this.oImage.nodeType == 1) + this.oImg = this.oImage.getElementsByTagName("img")[0]; + if (this.localName == "preview") { + var _self = this; + this.$ext.onclick = function() { + if (!_self.sPreview) return; + _self.$ext.innerHTML = _self.sPreview; + this.onclick = null; + }; + } + + var _self = this; + apf.addListener(this.$ext, "mouseover", function(e) { + if (!_self.disabled) + _self.dispatchEvent("mouseover", {htmlEvent: e}); + }); + + apf.addListener(this.$ext, "mouseout", function(e) { + if (!_self.disabled) + _self.dispatchEvent("mouseout", {htmlEvent: e}); + }); + }; + + this.addEventListener("DOMNodeInsertedIntoDocument", function() { + var node, + val = "", + i = this.childNodes.length; + + for (; i >= 0; --i) { + if ((node = this.childNodes[i]) && node.nodeName + && node.nodeName == "#cdata-section") { + val = node.nodeValue; + node.removeNode(); + } + } + + this.sPreview = val; + }); + + this.$resize = function(){ + var diff = apf.getDiff(this.$ext); + var wratio = 1, hratio = 1; + + this.oImg.style.width = ""; + this.oImg.style.height = ""; + + if (this.oImg.offsetWidth > this.$ext.offsetWidth) + wratio = this.oImg.offsetWidth / (this.$ext.offsetWidth - diff[0]); + if (this.oImg.offsetHeight > this.$ext.offsetHeight) + hratio = this.oImg.offsetHeight / (this.$ext.offsetHeight - diff[1]); + + if (wratio > hratio && wratio > 1) + this.oImg.style.width = "100%"; + else if (hratio > wratio && hratio > 1) + this.oImg.style.height = "100%"; + + this.oImg.style.top = ((this.$ext.offsetHeight - apf.getHeightDiff(this.$ext) + - this.oImg.offsetHeight) / 2) + "px"; + } +}).call(apf.img.prototype = new apf.BaseSimple()); + +apf.preview.prototype = apf.img.prototype; + +apf.aml.setElement("img", apf.img); +apf.aml.setElement("preview", apf.preview); + +apf.aml.setElement("name", apf.BindingRule); +apf.aml.setElement("image", apf.BindingRule); + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/elements/item.js)SIZE(23478)TIME(Tue, 13 Dec 2011 13:33:58 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + +/** + * Item of a menu displaying a clickable area. + * Example: + * + * + * Tutorials + * Contact + * + * + * + * File + * + * + * + * @define item + * @constructor + * + * @event click Fires when a user presses the mouse button while over this element. + * object: + * {XMLElement} xmlContext the xml data node that was selected in the opener at the time of showing the context menu. + * {AMLElement} opener the element that was clicked upon when showing the context menu. + */ +apf.item = function(struct, tagName){ + this.$init(tagName || "item", apf.NODE_VISIBLE, struct); +}; + +(function(){ + this.$focussable = false; + this.$childProperty = "caption"; + this.$canLeechSkin = "item"; + + this.checked = false; + this.selected = false; + + this.implement(apf.ChildValue); + + /**** Properties and Attributes ****/ + + //1 = force no bind rule, 2 = force bind rule + this.$attrExcludePropBind = apf.extend({ + "match" : 1 + }, this.$attrExcludePropBind); + + this.$booleanProperties["checked"] = true; + this.$booleanProperties["selected"] = true; + + this.$supportedProperties.push("submenu", "value", "match", "group", "icon", + "checked", "selected", "disabled", "caption", + "type"); + + /** + * @attribute {String} [submenu] the id of the menu that is shown + * when the user hovers over this menu item. + * Example: + * + * + * test + * test2 + * + * + * + * Sub menu + * + * + * + * + * File + * + * + * + */ + this.$propHandlers["submenu"] = function(value){ + apf.setStyleClass(this.$ext, "submenu"); + } + + /** + * @attribute {String} value the value of this element. + */ + + /** + * @attribute {String} [select] the xpath statement which works on the + * xml context of the parent menu element to determine whether this + * item is shown. + * Example: + * This example shows a list + * + * + * Send an E-mail + * Call Number + * + * Remove + * + * View Pictures + * + * + * + * Reboot + * + * + * + * Please right-click on this plane + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + */ + this.$propHandlers["select"] = function(value){ + this.select = value + ? "self::" + value.split("|").join("|self::") + : value; + } + + /** + * @attribute {String} [group] the name of the group this item belongs + * to. + * Example: + * + * + * item 1 + * item 2 + * item 3 + * item 4 + * + * + */ + this.$propHandlers["group"] = function(value){ + if (this.$group && this.$group.$removeRadio) + this.$group.$removeRadio(this); + + if (!value) { + this.$group = null; + return; + } + + var group = typeof value == "string" + ? + + apf.nameserver.get("group", value) + + : value; + if (!group) { + + group = apf.nameserver.register("group", value, + new apf.$group()); + group.setAttribute("id", value); + group.dispatchEvent("DOMNodeInsertedIntoDocument"); + group.parentNode = this; + + } + this.$group = group; + + this.$group.$addRadio(this); + }; + + + /** + * @attribute {String} hotkey the key combination a user can press + * to active the function of this element. Use any combination of + * Ctrl, Shift, Alt, F1-F12 and alphanumerical characters. Use a + * space, a minus or plus sign as a seperator. + * Example: + * + * Quit + * + */ + this.$propHandlers["hotkey"] = function(value){ + if (this.$hotkey) + apf.setNodeValue(this.$hotkey, apf.isMac ? apf.hotkeys.toMacNotation(value) : value); + + if (this.$lastHotkey) { + apf.hotkeys.remove(this.$lastHotkey[0], this.$lastHotkey[1]); + delete this.$lastHotkey[0]; + } + + if (value) { + this.$lastHotkey = [value]; + var _self = this; + apf.hotkeys.register(value, this.$lastHotkey[1] = function(){ + if (_self.disabled || !_self.visible) + return; + + //hmm not very scalable... + var buttons = apf.document.getElementsByTagNameNS(apf.ns.aml, "button"); + for (var i = 0; i < buttons.length; i++) { + if (buttons[i].submenu == _self.parentNode.name) { + var btn = buttons[i]; + btn.$setState("Over", {}); + + $setTimeout(function(){ + btn.$setState("Out", {}); + }, 200); + + break; + } + } + + _self.$down(); + _self.$up(); + _self.$click(); + }); + } + } + + /** + * @attribute {String} icon the url of the image used as an icon or + * a reference to an iconmap. + */ + this.$propHandlers["icon"] = function(value){ + if (this.$icon) + apf.skins.setIcon(this.$icon, value, this.parentNode.iconPath); + } + + /** + * @attribute {String} caption the text displayed on the item. + */ + this.$propHandlers["caption"] = function(value){ + if (this.$caption) + apf.setNodeValue(this.$caption, value); + } + + /** + * @attribute {String} type the function of this item + * Possible values: + * item + * check + * radio + */ + this.$propHandlers["type"] = function(value){ + apf.setStyleClass(this.$ext, value, ["item", "check", "radio"]); + } + + this.$values = [1, 0]; + this.$propHandlers["values"] = function(value){ + this.$values = value && value.split("|"); + } + + this.$propHandlers["value"] = function(value){ + if (this.type != "check") + return; + + this.setProperty("checked", this.$values.indexOf(value) == 0); + } + + /** + * @attribute {Boolean} checked whether the item is checked. + */ + this.$propHandlers["checked"] = function(value){ + if (this.type != "check") + return; + + if (apf.isTrue(value)) + apf.setStyleClass(this.$ext, "checked"); + else + apf.setStyleClass(this.$ext, "", ["checked"]); + + if (this.$values && this.$values[value ? 0 : 1] != this.value) + return this.setProperty("value", this.$values[value ? 0 : 1]); + } + + this.select = function(){ + this.parentNode.select(this.group, this.value || this.caption); + } + + this.check = function(){ + this.setProperty("checked", true); + } + this.uncheck = function(){ + this.setProperty("checked", false); + } + + this.$check = function(){ + apf.setStyleClass(this.$ext, "selected"); + } + + this.$uncheck = function(){ + apf.setStyleClass(this.$ext, "", ["selected"]); + } + + /** + * @attribute {Boolean} selected whether the item is selected. + */ + this.$propHandlers["selected"] = function(value){ + if (this.type != "radio") + return; + + + if (apf.isTrue(value)) { + if (this.$group) + this.$group.setProperty("value", this.value); + this.$check(); + } + else + this.$uncheck(); + } + + /** + * @attribute {Boolean} disabled whether the item is active. + */ + this.$propHandlers["disabled"] = function(value){ + if (apf.isTrue(value) || value == -1) + apf.setStyleClass(this.$ext, "disabled"); + else + apf.setStyleClass(this.$ext, "", ["disabled"]); + } + + /**** Dom Hooks ****/ + + //@todo apf3.0 + this.addEventListener("AMLReparent", function(beforeNode, pNode, withinParent){ + if (!this.$amlLoaded) + return; + + if (!withinParent && this.skinName != pNode.skinName) { + //@todo for now, assuming dom garbage collection doesn't leak + this.loadAml(); + } + }); + + /**** Events ****/ + + this.$down = function(){ + + }; + + this.$up = function(){ + if (this.type == "radio") + this.parentNode.select(this.group, this.value || this.caption); + + else if (this.type == "check") { + this.setProperty("checked", !this.checked); + //this.$handlePropSet("checked", !this.checked); + } + + if (this.submenu) { + this.$over(null, true); + return; + } + + this.parentNode.$hideTree = true; + + //@todo This statement makes the menu loose focus. + this.parentNode.hide();//true not focus?/ + + this.parentNode.dispatchEvent("itemclick", { + value : this.value || this.caption, + relatedNode : this, + checked : this.checked, + selected : this.selected + }); + + //@todo Anim effect here? + + this.dispatchEvent("click", { + xmlContext : this.parentNode.xmlReference, + opener : this.parentNode.opener + }); + }; + + this.$click = function(){ + + }; + + var timer; + this.$out = function(e){ + if (apf.isChildOf(this.$ext, e.toElement || e.explicitOriginalTarget) + || apf.isChildOf(this.$ext, e.srcElement || e.target)) //@todo test FF + return; + + clearTimeout(timer); + if (!this.submenu || this.$submenu(true)) { + apf.setStyleClass(this.$ext, "", ['hover']); + + var sel = this.parentNode.$selected; + if (sel && sel != this) + apf.setStyleClass(sel.$ext, "", ["hover"]); + + this.parentNode.$selected = null; + } + }; + + this.$over = function(e, force){ + if (this.parentNode.$selected == this && e) + return; + + if (this.parentNode.$selected) + apf.setStyleClass(this.parentNode.$selected.$ext, "", ["hover"]); + + apf.setStyleClass(this.$ext, "hover"); + this.parentNode.$selected = this; + + if (!force && (apf.isChildOf(this.$ext, e.toElement || e.explicitOriginalTarget) + || apf.isChildOf(this.$ext, e.fromElement || e.target))) //@todo test FF + return; + + var _self = this, ps = this.parentNode.$showingSubMenu; + if (ps && ps.name == this.submenu) { + this.parentNode.$selected = null; + this.parentNode.$showingSubMenu = null; + _self.$submenu(); + return; + } + + clearTimeout(timer); + + function submenu(){ + if (ps && ps.visible) { + ps.hide(); + + if (_self.parentNode.$showingSubMenu == ps) + _self.parentNode.$showingSubMenu = null; + } + + if (_self.submenu && _self.parentNode.opener + && _self.parentNode.opener.visible) + _self.$submenu(); + } + + if (force) + submenu(); + else { + timer = $setTimeout(function(){ + submenu(); + timer = null; + }, 210); + } + }; + + this.$submenu = function(hide, force){ + if (!this.submenu) + return true; + + var menu = self[this.submenu]; + if (!menu) { + + + return; + } + + if (!hide) { + //if (this.parentNode.showingSubMenu == this.submenu) + //return; + + this.parentNode.$showingSubMenu = menu; + + var pos = apf.getAbsolutePosition(this.$ext, this.parentNode.$ext.offsetParent); + menu.display(pos[0] + this.$ext.offsetWidth - 3, + pos[1] + 3, true, this, + this.parentNode.xmlReference, this.parentNode.$uniqueId); + menu.setAttribute("zindex", (this.parentNode.zindex || this.parentNode.$ext.style.zIndex || 1) + 1); + } + else { + if (menu.visible && !force) { + return false; + } + + if (this.parentNode.$showingSubMenu == menu) + this.parentNode.$showingSubMenu = null; + + apf.setStyleClass(this.$ext, '', ['hover']); + menu.hide(); + return true; + } + }; + + /**** Init ****/ + + this.$draw = function(isSkinSwitch){ + var p = this.parentNode; + while (p.$canLeechSkin == "item") + p = p.parentNode; + + //@todo apf3.0 rename doesnt work yet. + //@todo apf3.0 implement DOM Mutation events for multiselect widgets + //@todo apf3.0 implement attribute change triggers for icon, image, value, caption to updateNode this.$container + //@todo apf3.x this should be rearchitected + //@todo apf3.x the functions dont need to be overloaded if selectNodes would work properly + if (p.hasFeature(apf.__MULTISELECT__)) { + var _self = this; + + //@todo DOMNodeInserted should reset this + //@todo DOMNodeRemoved should reset this + if (!this.$hasSetSkinListener) { + var f; + this.parentNode.addEventListener("$skinchange", f = function(){ + if (_self.$amlDestroyed) //@todo apf3.x + return; + + if (_self.$ext.parentNode) + this.$deInitNode(_self, _self.$ext); + + var oInt = p == _self.parentNode ? p.$container : _self.parentNode.$container; + var node = oInt.lastChild;//@todo this should be more generic + p.$add(_self, _self.getAttribute(apf.xmldb.xmlIdTag) + "|" + this.$uniqueId, + _self.parentNode, oInt != p.$container && oInt, null); + p.$fill(); + + if (p.$isTreeArch) { + _self.$container = p.$getLayoutNode("item", "container", + _self.$ext = node && node.nextSibling || oInt.firstChild);//@todo this should be more generic + } + else _self.$ext = node && node.nextSibling || oInt.firstChild; + + var ns = _self; + while((ns = ns.nextSibling) && ns.nodeType != 1); + + if (!ns || ns.$canLeechSkin != "item") + p.dispatchEvent("afterload"); + }); + this.addEventListener("DOMNodeRemoved", function(e){ + if (e.currentTarget == this) + this.parentNode.removeEventListener("$skinchange", f); + }); + + this.$hasSetSkinListener = true; + } + + if (!p.$itemInited) { + p.canrename = false; //@todo fix rename + p.$removeClearMessage(); //@todo this should be more generic + p.$itemInited = [p.getTraverseNodes, p.getFirstTraverseNode, p.getTraverseParent]; + + p.getTraverseNodes = function(xmlNode){ + return (xmlNode || p).getElementsByTagNameNS(apf.ns.apf, "item"); + } + p.getFirstTraverseNode = function(xmlNode){ + return (xmlNode || p).getElementsByTagNameNS(apf.ns.apf, "item")[0]; + } + p.getTraverseParent = function(xmlNode){ + return xmlNode && xmlNode.parentNode; + } + p.each = (this.prefix ? this.prefix + ":" : "") + "item"; + + //@todo this is all an ugly hack (copied to baselist.js line 868) + p.$preventDataLoad = true;//@todo apf3.0 add remove for this + + p.$initingModel = true; + p.$setDynamicProperty("icon", "[@icon]"); + p.$setDynamicProperty("image", "[@image]"); + p.$setDynamicProperty("caption", "[label/text()|@caption|text()]"); + p.$setDynamicProperty("eachvalue", "[value/text()|@value|text()]"); + p.$canLoadDataAttr = false; + + if (!p.xmlRoot) + p.xmlRoot = p; + } + + this.$loadAml = function(){ + //hack + if (!this.getAttribute("caption")) + this.setAttribute("caption", this.caption); + + var oInt = p == this.parentNode ? p.$container : this.parentNode.$container; + var node = oInt.lastChild;//@todo this should be more generic + if (!p.documentId) + p.documentId = apf.xmldb.getXmlDocId(this); + p.$add(this, apf.xmldb.nodeConnect(p.documentId, this, null, p), + this.parentNode, oInt != p.$container && oInt, null); + p.$fill(); + + if (p.$isTreeArch) { + this.$container = p.$getLayoutNode("item", "container", + this.$ext = node && node.nextSibling || oInt.firstChild);//@todo this should be more generic + } + else this.$ext = node && node.nextSibling || oInt.firstChild; + + var ns = this; + while((ns = ns.nextSibling) && ns.nodeType != 1); + + if (!ns || ns.$canLeechSkin != "item") { + p.dispatchEvent("afterload"); + if (p.autoselect) + p.$selectDefault(this.parentNode); + } + } + + return; + } + + this.$ext = this.$getExternal(this.$isLeechingSkin + ? "item" //this.type + : "main", null, function($ext){ + var o = 'var o = apf.lookup(' + this.$uniqueId + '); if (o.disabled) return; o'; + $ext.setAttribute("onmouseup", o + '.$up(event)'); + $ext.setAttribute("onmousemove", o + '.$over(event)'); + $ext.setAttribute("onmouseout", o + '.$out(event)'); + $ext.setAttribute("onmousedown", o + '.$down()'); + $ext.setAttribute("onclick", o + '.$click()'); + }); + + /*p.$getNewContext("item"); + var elItem = p.$getLayoutNode("item");*/ + + //@todo if not elItem try using own skin + + //this.$ext = apf.insertHtmlNode(elItem, this.parentNode.$container); + this.$caption = this.$getLayoutNode("item", "caption", this.$ext) + this.$icon = this.$getLayoutNode("item", "icon", this.$ext); + this.$hotkey = this.$getLayoutNode("item", "hotkey", this.$ext); + + if (!isSkinSwitch && this.nextSibling && this.nextSibling.$ext) + this.$ext.parentNode.insertBefore(this.$ext, this.nextSibling.$ext); + }; + + /** + * @private + */ + this.addEventListener("DOMNodeInsertedIntoDocument", function(e){ + //var x = this.$aml; + + //this.skinName = this.parentNode.skinName; + var isSkinSwitch = this.$ext ? true : false; + if (isSkinSwitch) { + if (typeof this.checked !== "undefined") + this.$handlePropSet("checked", this.checked); + else if (typeof this.selected !== "undefined") + this.$handlePropSet("selected", this.selected); + + if (this.disabled) + this.$handlePropSet("disabled", this.disabled); + + if (this.caption) + this.$handlePropSet("caption", this.caption); + } + }); +}).call(apf.item.prototype = new apf.Presentation()); + +//apf.aml.setElement("radio", apf.radio); +//apf.aml.setElement("check", apf.check); +apf.aml.setElement("item", apf.item); + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/elements/junction.js)SIZE(2555)TIME(Fri, 14 Oct 2011 08:01:44 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + + +/** + * A symbolic link to an AML element. The visibility of this element determines + * the true parent of the references AML element. Multiple junctions will compete + * to determine the parent. Make sure only one junction reference is visible + * at the same time. + * + * @author Ruben Daniels (ruben AT ajax DOT org) + * @version %I%, %G% + * @since 0.4 + */ +apf.junction = function(){ + this.$init("junction", apf.NODE_HIDDEN); +}; + +(function(){ + this.$focussable = false; + + this.autoshow = true; + + /** + * @attribute {String} for + * @attribute {String} autoshow + */ + this.$booleanProperties["autoshow"] = true; + + this.$propHandlers["for"] = function(value){ + if (this.$amlLoaded) //@todo remove vManager + init.call(this); + } + + function init(e){ + var _self = this; + if (apf.window.vManager.permanent(this.parentNode, function(){ + //Show + _self.$reparent(); + }, function(){ + //Hide + + })) { + this.$reparent(); + } + } + + this.$reparent = function(){ + var amlNode = self[this["for"]]; + if (!amlNode) + return; //@todo warn? + + if (this.autoshow) + amlNode.show(); + + this.parentNode.insertBefore(amlNode, this); + } + + this.addEventListener("DOMNodeInsertedIntoDocument", init); +}).call(apf.junction.prototype = new apf.AmlElement()); + +apf.aml.setElement("junction", apf.junction); + + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/elements/label.js)SIZE(4978)TIME(Fri, 14 Oct 2011 08:01:44 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + +/** + * Element displaying a text in the user interface, usually specifying + * a description of another element. When the user clicks on the label it + * can set the focus to the connected aml element. + * Example: + * This example uses the for attribute to connect the label to the form element. + * + * Address + * + * + * + * @constructor + * @allowchild {smartbinding} + * @addnode elements + * + * @inherits apf.BaseSimple + * + * @author Ruben Daniels (ruben AT ajax DOT org) + * @version %I%, %G% + * @since 0.4 + * + * @binding value Determines the way the value for the element is retrieved + * from the bound data. + * Example: + * Sets the label text based on data loaded into this component. + * + * + * + * + * + * + * Example: + * A shorter way to write this is: + * + * + * + * + * + * + */ +apf.label = function(struct, tagName){ + this.$init(tagName || "label", apf.NODE_VISIBLE, struct); +}; + +(function(){ + this.implement( + + apf.DataAction, + + apf.ChildValue + ); + + var _self = this; + + this.$focussable = false; + var forElement; + + + + /** + * Sets the value of this element. This should be one of the values + * specified in the values attribute. + * @param {String} value the new value of this element + */ + this.setValue = function(value){ + this.setProperty("value", value, false, true); + }; + + /** + * Returns the current value of this element. + * @return {String} + */ + this.getValue = function(){ + return this.value; + } + + + + /** + * @attribute {String} caption the text displayed in the area defined by this + * element. Using the value attribute provides an alternative to using + * the text using a text node. + * + * @attribute {String} for the id of the element that receives the focus + * when the label is clicked on. + */ + this.$supportedProperties.push("caption", "for", "textalign"); + this.$propHandlers["caption"] = function(value){ + this.$caption.innerHTML = value; + }; + this.$propHandlers["for"] = function(value){ + forElement = typeof value == "string" ? self[value] : value; + }; + this.$propHandlers["textalign"] = function(value){ + this.$caption.style.textAlign = value || ""; + }; + + this.$draw = function(){ + //Build Main Skin + this.$ext = this.$getExternal(); + this.$caption = this.$getLayoutNode("main", "caption", this.$ext); + if (this.$caption.nodeType != 1) + this.$caption = this.$caption.parentNode; + + this.$ext.onmousedown = function(){ + if (forElement && forElement.$focussable && forElement.focussable) + forElement.focus(); + } + + var _self = this; + apf.addListener(this.$ext, "click", function(e) { + if (!_self.disabled) + _self.dispatchEvent("click", {htmlEvent: e}); + }); + + apf.addListener(this.$ext, "mouseover", function(e) { + if (!_self.disabled) + _self.dispatchEvent("mouseover", {htmlEvent: e}); + }); + + apf.addListener(this.$ext, "mouseout", function(e) { + if (!_self.disabled) + _self.dispatchEvent("mouseout", {htmlEvent: e}); + }); + }; + + this.$childProperty = "caption"; + +}).call(apf.label.prototype = new apf.BaseSimple()); + +apf.aml.setElement("label", apf.label); + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/elements/lineselect.js)SIZE(4747)TIME(Fri, 14 Oct 2011 08:01:44 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/elements/list.js)SIZE(14336)TIME(Fri, 14 Oct 2011 08:01:44 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + +/** + * Element displaying a skinnable list of options which can be selected. + * Selection of multiple items can be allowed. Items can be renamed + * and removed. The list can be used as a collection of checkboxes or + * radiobuttons. This is especially useful for use in forms. + * This element is one of the most often used elements. It can display lists + * of items in a cms style interface or display a list of search results in + * a more website like interface. + * Example: + * A simple list with inline items. + * + * + * The Netherlands + * United States of America + * United Kingdom + * ... + * + * + * Example: + * A databound list with items loaded from an xml file. + * + * + * + * Example: + * A databound list using the bindings element + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * @event click Fires when a user presses a mouse button while over this element. + * + * @constructor + * @define list, select, select1, thumbnail + * @allowchild {smartbinding} + * @addnode elements + * + * @inherits apf.BaseList + * @inherits apf.Rename + * + * @author Ruben Daniels (ruben AT ajax DOT org) + * @version %I%, %G% + * @since 0.4 + */ +apf.list = function(struct, tagName){ + this.$init(tagName || "list", apf.NODE_VISIBLE, struct); +}; + +/** + * Example: + * A small product search application using a list to display results. + * + * + *

Search for a product

+ * + * Search + *
+ * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * function search(){ + * mdlSearch.$loadFrom("http://localhost/search.php?keyword=" + txtSearch.getValue()); + * } + * + *
+ */ +apf.thumbnail = function(struct, tagName){ + this.$init(tagName || "thumbnail", apf.NODE_VISIBLE, struct); +}; + +apf.select = function(struct, tagName){ + this.$init(tagName || "select", apf.NODE_VISIBLE, struct); +}; + +apf.select1 = function(struct, tagName){ + this.$init(tagName || "selectl", apf.NODE_VISIBLE, struct); +}; + +(function(){ + this.morePos = "end"; + + + if (!apf.isIphone) + this.implement(apf.Rename); + + + + this.$getCaptionElement = function(){ + if (!(this.$caret || this.$selected)) + return; + + var x = this.$getLayoutNode("item", "caption", this.$caret || this.$selected); + if (!x) + return; + return x.nodeType == 1 ? x : x.parentNode; + }; + + + + + + + + /**** Properties and Attributes ****/ + + this.$supportedProperties.push("appearance", "mode", "more", "thumbsize", "morepos"); + + this.$propHandlers["morepos"] = function(value) { + this.morePos = value; + }; + + this.$propHandlers["thumbsize"] = function(value){ + var className = this.thumbclass; + + if (apf.isIE) { //@todo detection?? + className = className.splitSafe(","); + for (var i = 0; i < className.length; i++) { + apf.setStyleRule(className[i], "width", value + "px"); + apf.setStyleRule(className[i], "height", value + "px"); + } + return; + } + + apf.setStyleRule(className, "width", value + "px"); + apf.setStyleRule(className, "height", value + "px"); + }; + + + /** + * @attribute {String} appearance the type of select this element is. + * This is an xforms property and only available if apf is compiled + * with __WITH_XFORMS set to 1. + * Possible values: + * full depending on the tagName this element is either a list of radio options or of checked options. + * compact this elements functions like a list with multiselect off. + * minimal this element functions as a dropdown element. + */ + this.$propHandlers["appearance"] = function(value){ + + }; + + + /** + * @attribute {String} more Adds a new item to the list and lets the users + * type in the new name. This is especially useful in the interface when + * {@link element.list.attribute.mode} is set to check or radio. For instance in a form. + * Example: + * This example shows a list in form offering the user several options. The + * user can add a new option. A server script could remember the addition + * and present it to all new users of the form. + * + * + * + * + * Suggestion 1 + * Suggestion 2 + * + * + * + * Which newspapers do you read? + * + * + * + * + * + * + * + * + * + * + * New Answer + * + * + * + * + */ + this.$propHandlers["more"] = function(value){ + if (value) { + this.delayedselect = false; + this.addEventListener("xmlupdate", $xmlUpdate); + this.addEventListener("afterload", $xmlUpdate); + //this.addEventListener("afterrename", $afterRenameMore); + //this.addEventListener("beforeselect", $beforeSelect); + + this.$addMoreItem = function(msg){ + if (!this.moreItem) + this.$fill(); + if (this.morePos == "begin") + this.$container.insertBefore(this.moreItem, this.$container.firstChild); + else + this.$container.appendChild(this.moreItem); + }; + this.$updateClearMessage = function(){} + this.$removeClearMessage = function(){}; + } + else { + this.removeEventListener("xmlupdate", $xmlUpdate); + this.removeEventListener("afterload", $xmlUpdate); + //this.removeEventListener("afterrename", $afterRenameMore); + //this.removeEventListener("beforeselect", $beforeSelect); + } + }; + + function $xmlUpdate(e){ + if ((!e.action || "insert|add|synchronize|move".indexOf(e.action) > -1) && this.moreItem) { + if (this.morePos == "begin") + this.$container.insertBefore(this.moreItem, this.$container.firstChild); + else + this.$container.appendChild(this.moreItem); + } + } + + /*function $afterRenameMore(){ + var caption = this.$applyBindRule("caption", this.caret) + var xmlNode = this.findXmlNodeByValue(caption); + + var curNode = this.caret; + if (xmlNode != curNode || !caption) { + if (xmlNode && !this.isSelected(xmlNode)) + this.select(xmlNode); + this.remove(curNode); + } + else + if (!this.isSelected(curNode)) + this.select(curNode); + } + + function $beforeSelect(e){ + //This is a hack + if (e.xmlNode && this.isSelected(e.xmlNode) + && e.xmlNode.getAttribute('custom') == '1') { + this.setCaret(e.xmlNode); + this.selected = e.xmlNode; + $setTimeout(function(){ + _self.startRename() + }); + return false; + } + }*/ + + + /**** Keyboard support ****/ + + + this.addEventListener("keydown", this.$keyHandler, true); + + + /**** Init ****/ + + this.$draw = function(){ + this.appearance = this.getAttribute("appearance") || "compact"; + + //Build Main Skin + this.$ext = this.$getExternal(); + this.$container = this.$getLayoutNode("main", "container", this.$ext); + + if (apf.hasCssUpdateScrollbarBug && !this.mode) + this.$fixScrollBug(); + + var _self = this; + this.$ext.onclick = function(e){ + _self.dispatchEvent("click", { + htmlEvent: e || event + }); + } + + + + //Get Options form skin + //Types: 1=One dimensional List, 2=Two dimensional List + this.listtype = parseInt(this.$getOption("main", "type")) || 1; + //Types: 1=Check on click, 2=Check independent + this.behaviour = parseInt(this.$getOption("main", "behaviour")) || 1; + + this.thumbsize = this.$getOption("main", "thumbsize"); + this.thumbclass = this.$getOption("main", "thumbclass"); + }; + + this.$loadAml = function(x){ + }; + + this.$destroy = function(){ + if (this.$ext) + this.$ext.onclick = null; + apf.destroyHtmlNode(this.oDrag); + this.oDrag = null; + }; +}).call(apf.list.prototype = new apf.BaseList()); + +apf.thumbnail.prototype = +apf.select.prototype = +apf.select1.prototype = apf.list.prototype; + +apf.aml.setElement("thumbnail", apf.thumbnail); +apf.aml.setElement("select", apf.select); +apf.aml.setElement("select1", apf.select1); +apf.aml.setElement("list", apf.list); + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/elements/loader.js)SIZE(3558)TIME(Wed, 30 Nov 2011 17:29:20 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + + +/** + * @todo description + * + * @author Ruben Daniels (ruben AT ajax DOT org) + * @version %I%, %G% + * @since 0.4 + */ +apf.loader = function(){ + this.$init("loader", apf.NODE_HIDDEN); + + this.show = function(){ + this.$ext.style.display = "block"; + } + + this.hide = function(){ + this.$ext.style.display = "none"; + } + + this.addEventListener("DOMNodeInsertedIntoDocument", function(e){ + var pHtmlNode; + if (!(pHtmlNode = this.parentNode.$int)) + return; + + this.$ext = apf.insertHtmlNode(null, pHtmlNode, null, (this.$aml + ? (this.$aml.serialize ? this.$aml.serialize() : this.$aml.xml) + : this.serialize()).replace(/^<[^>]*>\s*|\s*<[^>]*>$/g, "")); + + if (!apf.loaded) { + var _self = this; + if(apf.config.loaderAnimIndicator) + _self.animateLoader(10); + apf.addEventListener("load", function(){ + if (apf.isTrue(apf.config.autoHideLoading)) { + apf.queue.empty(); + _self.hide(); + } + else { + if(apf.config.loaderAnimIndicator) + _self.animateLoader(20); + } + }); + } + }); + + this.lastLoaderStep = 0; + + /* + * Animates a loader indiacator + * + * the step is in the % of the total width of the indicator + * + */ + this.animateLoader = function(step){ + var _self = this, + loaderEl = document.getElementById(apf.config.loaderAnimIndicator); + if(!loaderEl) + return; + + step = apf.config.loaderWidth * (step/100); + + var fromStep = this.lastLoaderStep, + toStep = this.lastLoaderStep + step; + + this.lastLoaderStep = toStep; + + if(toStep > apf.config.loaderWidth) + toStep = apf.config.loaderWidth; + + apf.tween.single(document.getElementById('animatedLoader'), { + steps : 5, + anim : apf.tween.EASEOUT, + type : "width", + from : fromStep, + to : toStep, + onfinish : function() { + if(toStep >= apf.config.loaderWidth) { + apf.queue.empty(); + _self.hide(); + } + } + }); + }; +}; + +apf.loader.prototype = new apf.AmlElement(); + +apf.aml.setElement("loader", apf.loader); + + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/elements/loadindicator.js)SIZE(5234)TIME(Fri, 14 Oct 2011 08:01:44 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/elements/map.js)SIZE(21831)TIME(Fri, 14 Oct 2011 08:01:44 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/elements/markupedit.js)SIZE(55951)TIME(Fri, 14 Oct 2011 08:01:44 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/elements/menu.js)SIZE(19155)TIME(Mon, 19 Dec 2011 11:09:29 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + +/** + * Element displaying a skinnable menu of items which can be choosen. + * Based on the context of the menu, items can be shown and hidden. That's + * why this element is often called a contextmenu. + * Example: + * + * + * + * + * test + * test2 + * + * + * + * table_wizard + * table_wizard + * + * item 1 + * item 2 + * item 3 + * item 4 + * + * item check 1 + * item check 2 + * + * table_wizard + * table_wizard + * + * + * + * + * + * @see baseclass.guielement.event.contextmenu + * + * @event display Fires when the contextmenu is shown. + * @event itemclick Fires when a user presses the mouse button while over a child of this element. + * object: + * {String} value the value of the clicked element. + * + * @constructor + * @define menu + * @allowchild item, divider, check, radio + * @addnode elements + * + * @author Ruben Daniels (ruben AT ajax DOT org) + * @version %I%, %G% + * @since 0.4 + * + * @inherits apf.Presentation + */ +apf.menu = function(struct, tagName){ + this.$init(tagName || "menu", apf.NODE_VISIBLE, struct); + + this.animate = apf.enableAnim; +}; + +(function(){ + this.$focussable = apf.KEYBOARD; + this.$positioning = "basic" + //var _self = this; + //var blurring = false; + + /**** Properties and Attributes ****/ + + //this.zindex = 10000000; + this.visible = false; + this.matchhide = false; + + this.$booleanProperties["animate"] = true; + this.$booleanProperties["pinned"] = true; + this.$booleanProperties["matchhide"] = true; + + this.$propHandlers["visible"] = function(value, prop, force, nofocus, hideOpener){ + if (value) { + this.$ext.style.display = "block"; + if (this.opener && this.opener.localName.indexOf('item') > -1) + this.opener.parentNode.$showingSubMenu = this; + } + else { + this.$ext.style.display = "none"; + + var lastFocus = apf.menu.lastFocus; + var opener = this.opener; + //@todo test this with a list being the opener of the menu + if (lastFocus != this.opener && this.opener && this.opener.$blur) + this.opener.$blur(); + + if (this.opener && this.opener.parentNode && this.opener.parentNode.localName == "menu") { + if (!this.$hideTree) + this.$hideTree = -1 + this.opener.parentNode.focus(); + } + + + else if (lastFocus) { + //We're being hidden because some other object gets focus + if (apf.window.$settingFocus) { + if (apf.window.$settingFocus != lastFocus && lastFocus.$blur) + lastFocus.$blur(); + this.$blur(); + + if (apf.window.$settingFocus.localName != "menu") //not menu walking + apf.menu.lastFocus = null; + } + //We're being hidden because window looses focus + else if (!apf.window.hasFocus()) { + if (lastFocus.$blur) + lastFocus.$blur(); + this.$blur(); + + apf.document.activeElement = lastFocus; + if (lastFocus.$focusParent) + lastFocus.$focusParent.$lastFocussed = lastFocus; + + apf.menu.lastFocus = null; + } + //We're just being hidden + else if (this.$hideTree) { + if (!this.$hideTree) + this.$hideTree = -1 + + var visTest = (lastFocus.disabled || !lastFocus.visible) + && lastFocus != apf.document.documentElement; + + if (nofocus || visTest) { + if (lastFocus.$blur) + lastFocus.$blur(); + this.$blur(); + apf.document.activeElement = null; + + if (visTest && apf.window.moveNext() === false) + apf.window.$focusRoot(); + } + else { + lastFocus.focus(null, null, true); + } + + apf.menu.lastFocus = null; + } + } + + + clearTimeout(this.$submenuTimer); + + if (this.$showingSubMenu) { + this.$showingSubMenu.hide(); + this.$showingSubMenu = null; + } + + if (this.opener && this.opener.$submenu) { + this.opener.$submenu(true, true); + + //@todo problem with loosing focus when window looses focus + if (this.$hideTree === true && this.opener + && this.opener.parentNode && this.opener.parentNode.localName == "menu") { + this.opener.parentNode.$hideTree = true + this.opener.parentNode.hide(); + } + + this.opener = null; + } + this.$hideTree = null; + + if (this.$selected) { + apf.setStyleClass(this.$selected.$ext, "", ["hover"]); + this.$selected = null; + } + + this.dispatchEvent("hide", {opener: opener}); + } + }; + + /**** Public Methods ****/ + + var lastFocus; + + /** + * Shows the menu, optionally within a certain context. + * @param {Number} x the left position of the menu. + * @param {Number} y the top position of the menu. + * @param {Boolean} noanim whether to animate the showing of this menu. + * @param {AMLElement} opener the element that is the context of this menu. + * @param {XMLElement} xmlNode the {@link term.datanode data node} that provides data context to the menu child nodes. + * @see baseclass.guielement.event.contextmenu + */ + this.display = function(x, y, noanim, opener, xmlNode, openMenuId, btnWidth){ + this.opener = opener; + + //Show / hide Child Nodes Based on XML + if (xmlNode && !this.disabled) { + var last, i, node, + nodes = this.childNodes, + c = 0, + l = nodes.length, result; + for (i = 0; i < l; i++) { + node = nodes[i]; + if (node.nodeType != 1 || node.localName != "item") + continue; + + result = !xmlNode || !node.match || (node.cmatch || (node.cmatch = apf.lm.compile(node.match, { + xpathmode : 3, + injectself : true + })))(xmlNode) + + if (result) { + if (this.matchhide) + node.show(); + else + node.enable(); + + if (node.localName == "divider" && this.matchhide) { + last = node; + if (c == 0) + node.hide(); + c = 0; + } + else c++; + } + else { + if (this.matchhide) + node.hide(); + else + node.disable(); + + if (!node.nextSibling && c == 0 && last) + last.hide(); + } + } + } + + if (this.oOverlay) { + if (btnWidth) { + this.oOverlay.style.display = "block"; + this.oOverlay.style.width = btnWidth + "px"; + } + else + this.oOverlay.style.display = "none"; + } + + function afterRender(){ + if (x === null) { + apf.popup.show(this.$uniqueId, { + x : 0, + y : this.ref ? 0 : opener.$ext.offsetHeight, + animate : noanim || !this.animate ? false : "fade", + steps : 10, + ref : (this.ref || opener).$ext, + allowTogether: openMenuId, + autohide : !this.pinned, + noleft : this.left !== undefined, + setZindex : this.zindex ? false : true + }); + } + else { + var bodyPos = apf.getAbsolutePosition(document.body); + apf.popup.show(this.$uniqueId, { + x : x - bodyPos[0], + y : y - bodyPos[1] - (apf.isIE && apf.isIE < 8 ? 1 : 0), + animate : noanim || !this.animate ? false : "fade", + steps : 10, + //ref : this.$ext.offsetParent, + allowTogether: openMenuId, + autohide : !this.pinned, + setZindex : this.zindex ? false : true + //autoCorrect : false + }); + } + + var lastFocus = + apf.menu.lastFocus = opener && opener.$focussable === true + ? opener + : apf.menu.lastFocus || apf.document.activeElement; + + apf.popup.last = null; + + //if (!apf.isGecko) //This disables keyboard support for gecko - very strange behaviour + this.focus(); + + //Make the component that provides context appear to have focus + + if (lastFocus && lastFocus != this && lastFocus.$focus) + lastFocus.$focus(); + + this.xmlReference = xmlNode; + + //@todo consider renaming this to onshow and onhide + this.dispatchEvent("display", {opener: opener}); + } + + this.visible = false; + + if (this.$rendered !== false) { + this.show(); + afterRender.call(this); + } + else { + this.addEventListener("afterrender", afterRender); + this.show(); + } + }; + + /** + * Returns the current value of this element. + * @return {String} + */ + this.getValue = function(group){ + return this.getSelected(group).value || ""; + }; + + /** + * Retrieves the selected element from a group of radio elements. + * @param {String} group the name of the group. + * @return {radio} the selected radio element. + */ + this.getSelected = function(group){ + var nodes = this.childNodes; + var i, l = nodes.length; + for (i = 0; i < l; i++) { + if (nodes[i].group != group) + continue; + + if (nodes[i].selected) + return nodes[i]; + } + + return false; + } + + /** + * Selects an element within a radio group. + * @param {String} group the name of the group. + * @param {String} value the value of the item to select. + */ + this.select = function(group, value){ + var nodes = this.childNodes; + var i, l = nodes.length; + for (i = 0; i < l; i++) { + if (nodes[i].group != group) + continue; + + if (nodes[i].value == value || !nodes[i].value && nodes[i].caption == value) + nodes[i].setProperty("selected", true, false, true); + //nodes[i].$handlePropSet("selected", true); + else if (nodes[i].selected) + nodes[i].setProperty("selected", false, false, true); + //nodes[i].$handlePropSet("selected", false); + } + }; + + /**** Events ****/ + + + this.addEventListener("keydown", function(e){ + var node, key = e.keyCode; + //var ctrlKey = e.ctrlKey; + //var shiftKey = e.shiftKey; + + switch (key) { + case 13: + if (!this.$selected) + return; + + node = this.$selected; + node.$down(); + node.$up(); + node.$click(); + break; + case 27: + this.hide(); + break; + case 38: + //UP + node = this.$selected && this.$selected.previousSibling + || this.lastChild; + + if (node && node.localName == "divider") + node = node.previousSibling; + + if (!node) + return; + + if (this.$selected) + apf.setStyleClass(this.$selected.$ext, "", ["hover"]); + + apf.setStyleClass(node.$ext, "hover"); + this.$selected = node; + break; + case 40: + //DOWN + node = this.$selected && this.$selected.nextSibling + || this.firstChild; + + if (node && node.localName == "divider") + node = node.nextSibling; + + if (!node) + return; + + if (this.$selected) + apf.setStyleClass(this.$selected.$ext, "", ["hover"]); + + apf.setStyleClass(node.$ext, "hover"); + this.$selected = node; + break; + case 37: + //LEFT + //if (this.$selected && this.$selected.submenu) + //this.$selected.$submenu(true, true); + + if (!this.opener) + return; + + if (this.opener.localName == "button") { + node = this.opener.previousSibling; + while(node && !node.submenu) + node = node.previousSibling; + + if (node) { + node.dispatchEvent("mouseover"); + + var btnMenu = node.parentNode.menuIsPressed; + if (btnMenu) { + self[btnMenu.submenu].dispatchEvent("keydown", { + keyCode : 40 + }); + } + } + } + else if (this.opener.parentNode.localName == "menu") { + //@todo Ahum bad abstraction boundary + var op = this.opener; + this.hide(); + apf.setStyleClass(op.$ext, "hover"); + op.parentNode.$showingSubMenu = null; + } + + break; + case 39: + //RIGHT + if (this.$selected && this.$selected.submenu) { + this.$selected.$submenu(null, true); + this.$showingSubMenu.dispatchEvent("keydown", { + keyCode : 40 + }); + + return; + } + + if (this.opener) { + var op = this.opener; + while (op && op.parentNode && op.parentNode.localName == "menu") + op = op.parentNode.opener; + + if (op && op.localName == "button") { + node = op.nextSibling; + while(node && !node.submenu) + node = node.nextSibling; + + if (node) { + node.dispatchEvent("mouseover"); + + var btnMenu = node.parentNode.menuIsPressed; + if (btnMenu) { + self[btnMenu.submenu].dispatchEvent("keydown", { + keyCode : 40 + }); + } + + return; + } + } + } + + if (!this.$selected) { + arguments.callee.call(this, { + keyCode : 40 + }); + } + + break; + default: + return; + } + + return false; + }, true); + + + //Hide menu when it looses focus or when the popup hides itself + function forceHide(e){ + if (this.$showingSubMenu || this.pinned + || apf.isChildOf(e.fromElement, e.toElement) + || apf.isChildOf(this, e.toElement) || !e.toElement) + return; + + if (this.$hideTree != -1) { + this.$hideTree = true; + this.hide(); + } + + return false; + } + + this.addEventListener("focus", function(){ + apf.popup.last = this.$uniqueId; + }); + + this.addEventListener("blur", forceHide); + this.addEventListener("popuphide", forceHide); + + /**** Init ****/ + + this.$draw = function(){ + this.$pHtmlNode = document.body; + + //Build Main Skin + this.$ext = this.$getExternal(); + this.oOverlay = this.$getLayoutNode("main", "overlay", this.$ext); + + apf.popup.setContent(this.$uniqueId, this.$ext, "", null, null); + }; + + this.$loadAml = function(x){ + this.$int = this.$getLayoutNode("main", "container", this.$ext); + }; + + this.$destroy = function(){ + apf.popup.removeContent(this.$uniqueId); + }; +}).call(apf.menu.prototype = new apf.Presentation()); + +apf.aml.setElement("menu", apf.menu); + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/elements/method.js)SIZE(3973)TIME(Fri, 14 Oct 2011 08:01:44 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + + +/** + * element specifying a method available within the rpc element. + * + * @attribute {String} name the name of the method. This name will + * be available on the rpc object as a + * javascript method. + * Example: + * + * + * + * + * + * + * comm.save(data); + * + * + * @attribute {String} [callback] the name of the method that handles + * the return of the call. + * @attribute {Boolean} [async] whether the call is executed in the + * backround. Default is true. When set + * to false the application hangs while + * this call is executed. + * @attribute {Boolean} [caching] whether the call is cached. Default + * is false. When set to true any call + * with the same data will return immediately + * with the cached result. + * @attribute {Boolean} [ignore-offline] whether the method should not be stored + * for later execution when offline. + * @attribute {Boolean} [method-name] the name sent to the server. + * + * @attribute {String} [type] the type of the returned data + * Possible values: + * xml returns the response as an xml document + * + * @allowchild variable + */ +apf.method = function(struct, tagName){ + this.$init(tagName || "method", apf.NODE_HIDDEN, struct); + + this.async = true; + this.caching = false; + this["ignore-offline"] = false; +}; + +(function(){ + this.$parsePrio = "002"; + + this.$booleanProperties["async"] = true; + this.$booleanProperties["caching"] = true; + this.$booleanProperties["ignore-offline"] = true; + + this.$supportedProperties.push("name", "receive", "async", "caching", + "ignore-offline", "method-name", "type", "url"); + + this.$propHandlers["ignore-offline"] = function(value){ + this.ignoreOffline = value; + }; + + this.$propHandlers["method-name"] = function(value){ + this.methodName = value; + }; + + /**** DOM Handlers ****/ + + this.addEventListener("DOMNodeInserted", function(e){ + if (this.parentNode.$addMethod && e.currentTarget == this) + this.parentNode.$addMethod(this); + }); + + this.addEventListener("DOMNodeRemoved", function(e){ + if (this.parentNode.$removeMethod && e.currentTarget == this) + this.parentNode.$removeMethod(this); + }); + + this.addEventListener("DOMNodeInsertedIntoDocument", function(e){ + if (this.parentNode.$addMethod) + this.parentNode.$addMethod(this); + }); +}).call(apf.method.prototype = new apf.AmlElement()); + +apf.aml.setElement("method", apf.method); + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/elements/modalwindow.js)SIZE(24668)TIME(Tue, 06 Dec 2011 16:22:54 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + + +/** + * @private + */ +apf.WinServer = { + count : 9000, + wins : [], + + setTop : function(win, norecur){ + if (win.zindex || win.modal) + return; + + if (win.$opened) { + if (win.$opened.visible) + return; + else + delete win.$opened; + } + + var topmost; + if (!norecur && this.wins.length) { + var topmost = this.wins[this.wins.length - 1]; + if (topmost == win) + return; + + if (!topmost.modal || !topmost.visible) + topmost = null; + else if (topmost && win.modal) { + win.$opener = topmost; + topmost.$opened = win; + topmost = null; + } + } + + this.count += 2; + + win.setProperty("zindex", this.count); + this.wins.remove(win); + this.wins.push(win); + + if (topmost) + this.setTop(topmost, true); + + return win; + }, + + setNext : function(){ + if (this.wins.length < 2) return; + var nwin, start = this.wins.shift(); + do { + if (this.setTop(nwin || start).visible) + break; + nwin = this.wins.shift(); + } while (start != nwin); + }, + + setPrevious : function(){ + if (this.wins.length < 2) return; + this.wins.unshift(this.wins.pop()); + var nwin, start = this.wins.pop(); + do { + if (this.setTop(nwin || start).visible) + break; + nwin = this.wins.pop(); + } while (start != nwin); + }, + + remove : function(win){ + this.wins.remove(win); + } +} + +/** + * Element displaying a skinnable, draggable window with optionally + * a min, max, edit and close button. This element is also used + * as a portal widget container. Furthermore this element supports + * docking in an alignment layout. + * Example: + * + * + * + * + * + * @constructor + * @define modalwindow + * @allowchild {elements}, {smartbinding}, {anyaml} + * @addnode elements + * + * @author Ruben Daniels (ruben AT ajax DOT org) + * @version %I%, %G% + * @since 0.4 + * + * @inherits apf.Presentation + * @inherits apf.Transaction + * + * @event show Fires when the window is opened. + * @event close Fires when the window is closed. + * @event editstart Fires before the user edits the properties of this window. Used mostly for when this window is part of the {@link element.portal}. + * @event editstop Fires after the user edited the properties of this window. Used mostly for when this window is part of the {@link element.portal}. + * cancelable: Prevents the edit panel from being closed. + * @event statechange Fires after the state of this window changed. + * object: + * {Boolean} minimized whether the window is minimized. + * {Boolean} maximized whether the window is maximized. + * {Boolean} normal whether the window has it's normal size and position. + * {Boolean} edit whether the window is in the edit state. + * {Boolean} closed whether the window is closed. + */ +apf.toolwindow = function(struct, tagName){ + this.$init(tagName || "toolwindow", apf.NODE_VISIBLE, struct); +}; + +apf.modalwindow = function(struct, tagName){ + this.$init(tagName || "modalwindow", apf.NODE_VISIBLE, struct); +}; + +apf.AmlWindow = function(struct, tagName){ + this.$init(tagName || "window", apf.NODE_VISIBLE, struct); +}; + +(function(){ + this.implement( + apf.BaseStateButtons + ); + + this.$isWindowContainer = true; + this.collapsedHeight = 30; + this.canHaveChildren = 2; + this.visible = false; + this.showdragging = false; + this.kbclose = false; + this.$focussable = apf.KEYBOARD; + this.$editableCaption = ["title"]; + + /**** Public Methods ****/ + + + + /** + * Sets the title of the window. Call-chaining is supported. + * @param {String} caption the text of the title. + */ + this.setTitle = function(caption){ + this.setProperty("title", caption, false, true); + return this; + }; + + /** + * Sets the icon of the window. Call-chaining is supported. + * @param {String} icon the location of the image. + */ + this.setIcon = function(icon){ + this.setProperty("icon", icon, false, true); + return this; + }; + + //For modal elements + this.show = function(callback){ + this.execAction = callback; //@todo Proper error handling?? + this.setProperty("visible", true, false, true); + return this; + } + + + + + this.slideIn = function(sFrom, bSticky) { + if (!sFrom) + sFrom = "bottom"; + var _center = this.center; + this.center = false; + this.setProperty("visible", true); + this.center = _center; + + var iFrom = 0, + iTo = 0, + innerW = (apf.isIE + ? this.$ext.offsetParent.offsetWidth + : window.innerWidth), + innerH = (apf.isIE + ? this.$ext.offsetParent.offsetHeight + : window.innerHeight), + cX = Math.max(0, ((innerW - this.$ext.offsetWidth) / 2)), + cY = Math.max(0, ((innerH - this.$ext.offsetHeight) / 3)), + sType = "top", + pad = 10; + + switch(sFrom) { + case "top": + iFrom = -(this.$ext.offsetHeight) - pad; + iTo = bSticky ? 0 : cY; + break; + case "left": + iFrom = -(this.$ext.offsetWidth) - pad; + iTo = bSticky ? 0 : cX; + sType = "left"; + break; + case "bottom": + iFrom = innerH + this.$ext.offsetHeight + pad; + iTo = bSticky ? innerH - this.$ext.offsetHeight : cY; + break; + case "right": + iFrom = innerW + this.$ext.offsetLeft + pad; + iTo = bSticky ? innerW - this.$ext.offsetWidth : cX; + sType = "left"; + break; + } + + apf.tween.single(this.$ext, { + steps : apf.isIphone ? 5 : 30, + interval: 10, + from : iFrom, + to : iTo, + type : sType, + anim : apf.tween.EASEIN + }); + return this; + }; + + this.slideOut = function(sTo) { + if (!sTo) + sTo = "bottom"; + var iFrom = 0, + iTo = 0, + sType = "top", + pad = 10; + + switch(sTo) { + case "top": + iFrom = this.$ext.offsetTop; + iTo = -(this.$ext.offsetHeight) - pad; + break; + case "left": + iFrom = this.$ext.offsetLeft; + iTo = -(this.$ext.offsetWidth) - pad; + sType = "left"; + break; + case "bottom": + iFrom = this.$ext.offsetTop; + iTo = (apf.isIE + ? this.$ext.offsetParent.offsetHeight + : window.innerHeight) + this.$ext.offsetHeight + pad; + break; + case "right": + iFrom = this.$ext.offsetLeft; + iTo = (apf.isIE + ? this.$ext.offsetParent.offsetWidth + : window.innerWidth) + this.$ext.offsetLeft + pad; + sType = "left"; + break; + } + + var _self = this; + apf.tween.single(this.$ext, { + steps : apf.isIphone ? 5 : 30, + interval: 10, + from : iFrom, + to : iTo, + type : sType, + anim : apf.tween.EASEOUT, + onfinish: function() { _self.setProperty("visible", false); } + }); + return this; + }; + + + + this.bringToFront = function(){ + apf.WinServer.setTop(this); + return this; + }; + + /**** Properties and Attributes ****/ + + this.$booleanProperties["modal"] = true; + this.$booleanProperties["center"] = true; + this.$booleanProperties["transaction"] = true; + this.$booleanProperties["hideselects"] = true; + this.$booleanProperties["showdragging"] = true; + this.$booleanProperties["kbclose"] = true; + this.$supportedProperties.push("title", "icon", "modal", "minwidth", + "minheight", "hideselects", "center", "kbclose", + "maxwidth", "maxheight", "showdragging", "transaction"); + + /** + * @attribute {Boolean} modal whether the window prevents access to the + * layout below it. + */ + this.$propHandlers["modal"] = function(value){ + if (value) { + if (this.visible) + apf.plane.show(this.$ext, false, null, null, { + color : "black", + opacity : this.cover && this.cover.getAttribute("opacity") || 0.5, + protect : this.$uniqueId, + customCover : this.cover || "" + }); + } + else { + apf.plane.hide(this.$uniqueId); + } + }; + + /** + * @attribute {Boolean} center centers the window relative to it's parent's + * containing rect when shown. + */ + this.$propHandlers["center"] = function(value){ + this.$ext.style.position = "absolute"; //@todo no unset + }; + + /** + * @attribute {String} title the text of the title. + */ + this.$propHandlers["title"] = function(value){ + if (this.oTitle) + this.oTitle.nodeValue = value; + }; + + /** + * @attribute {String} icon the location of the image. + */ + this.$propHandlers["icon"] = function(value){ + if (!this.oIcon) return; + + this.oIcon.style.display = value ? "" : "none"; + apf.skins.setIcon(this.oIcon, value, this.iconPath); + }; + + this.$afterRender = function(){ + if (this.center && !this.left && !this.top && !this.right && !this.bottom && !this.anchors) { + + apf.layout.processQueue(); + + + var size = !this.$ext.offsetParent || this.$ext.offsetParent.tagName == "BODY" + ? [apf.getWindowWidth(), apf.getWindowHeight()] + : [this.$ext.offsetParent.offsetWidth, this.$ext.offsetParent.offsetHeight, 0, 0]; + + if (size.length == 2) { + size.push(document.documentElement.scrollLeft, + document.documentElement.scrollTop); + } + + //@todo it's better to add this to the layout queue + this.$ext.style.left = (Math.max(0, (( + size[0] - parseInt(this.$ext.offsetWidth || 0))/2)) + size[2]) + "px"; + this.$ext.style.top = (Math.max(0, (( + size[1] - parseInt(this.$ext.offsetHeight || 0))/3)) + size[3]) + "px"; + } + + + //@todo make widget a tagname and alias + if (this.$amlLoaded && (this.model + || (!this.dockable || !this.aData) && !this.$isWidget + && this.localName != "toolwindow")) + this.focus(false, {mouse:true}); + + + this.dispatchEvent("show"); + } + + var hEls = [], wasVisible; + this.$propHandlers["visible"] = function(value){ + if (apf.isTrue(value)){ + if (this.dispatchEvent("beforeshow") === false) + return (this.visible = false); + + if (this.modal){ + apf.plane.show(this.$ext, false, null, null, { + color : "black", + opacity : this.cover && this.cover.getAttribute("opacity") || 0.5, + protect : this.$uniqueId, + customCover : this.cover || "" + }); + } + + this.state = this.state.split("|").remove("closed").join("|"); + + this.$ext.style.display = ""; //Some form of inheritance detection + + //if (this.modal) + //this.$ext.style.position = "fixed"; + + if (!apf.canHaveHtmlOverSelects && this.hideselects) { + hEls = []; + var nodes = document.getElementsByTagName("select"); + for (var i = 0; i < nodes.length; i++) { + var oStyle = apf.getStyle(nodes[i], "display"); + hEls.push([nodes[i], oStyle]); + nodes[i].style.display = "none"; + } + } + + //if (this.modal) + //this.$ext.style.zIndex = apf.plane.$zindex - 1; + + if (apf.isIE) { + var cls = this.$ext.className; + this.$ext.className = "rnd" + Math.random(); + this.$ext.className = cls; + } + + if (this.$rendered === false) + this.addEventListener("afterrender", this.$afterRender); + else + this.$afterRender(); + } + else { + if (this.modal) + apf.plane.hide(this.$uniqueId); + + this.$ext.style.display = "none"; + + if (!apf.canHaveHtmlOverSelects && this.hideselects) { + for (var i = 0; i < hEls.length; i++) { + hEls[i][0].style.display = hEls[i][1]; + } + } + + if (this.hasFocus()) + apf.window.moveNext(true, this, true);//go backward to detect modals + + this.visible = false; + + this.dispatchEvent("hide"); + } + + + if (apf.layout && this.$int) + apf.layout.forceResize(this.$int); //@todo this should be recursive down + + + wasVisible = value; + }; + + this.$propHandlers["zindex"] = function(value){ + this.$ext.style.zIndex = value + 1; + }; + + /**** Keyboard ****/ + + + + this.addEventListener("keydown", function(e){ + var key = e.keyCode; + var ctrlKey = e.ctrlKey; + var shiftKey = e.shiftKey; + + /*if (key > 36 && key < 41) { + if (this.hasFeature && this.hasFeature(apf.__ANCHORING__)) + this.$disableAnchoring(); + }*/ + + var retValue = false; + switch (key) { + /*case 9: + break; + case 13: + break; + case 32: + break;*/ + case 38: + //UP + if (shiftKey && this.resizable) + this.setProperty("height", Math.max(this.minheight || 0, + this.$ext.offsetHeight - (ctrlKey ? 50 : 10))); + else if (this.draggable) + this.setProperty("top", + this.$ext.offsetTop - (ctrlKey ? 50 : 10)); + break; + case 37: + //LEFT + if (shiftKey && this.resizable) + this.setProperty("width", Math.max(this.minwidth || 0, + this.$ext.offsetWidth - (ctrlKey ? 50 : 10))); + else if (this.draggable) + this.setProperty("left", + this.$ext.offsetLeft - (ctrlKey ? 50 : 10)); + break; + case 39: + //RIGHT + if (shiftKey && this.resizable) + this.setProperty("width", Math.min(this.maxwidth || 10000, + this.$ext.offsetWidth + (ctrlKey ? 50 : 10))); + else if (this.draggable) + this.setProperty("left", + this.$ext.offsetLeft + (ctrlKey ? 50 : 10)); + break; + case 40: + //DOWN + if (shiftKey && this.resizable) + this.setProperty("height", Math.min(this.maxheight || 10000, + this.$ext.offsetHeight + (ctrlKey ? 50 : 10))); + else if (this.draggable) + this.setProperty("top", + this.$ext.offsetTop + (ctrlKey ? 50 : 10)); + break; + default: + retValue = null; + return; + } + + if (apf.hasSingleRszEvent) + apf.layout.forceResize(this.$int); + + return retValue; + }, true); + + this.addEventListener("keydown", function(e){ + if (e.keyCode == 27 && this.buttons.indexOf("close") > -1 + && (!this.dockable || !this.aData) && this.kbclose) + this.close(); + }); + + + + + /**** Init ****/ + + this.$draw = function(){ + this.popout = apf.isTrue(this.getAttribute("popout")); + if (this.popout) + this.$pHtmlNode = document.body; + + this.$ext = this.$getExternal(null, null, function(oExt){ + this.$initButtons(oExt); + }); + this.oTitle = this.$getLayoutNode("main", "title", this.$ext); + this.oIcon = this.$getLayoutNode("main", "icon", this.$ext); + this.oDrag = this.$getLayoutNode("main", "drag", this.$ext); + this.$buttons = this.$getLayoutNode("main", "buttons", this.$ext); + this.cover = this.$getLayoutNode("cover"); + + if (this.popout) + this.$ext.style.position = "absolute"; + + if (this.oIcon) + this.oIcon.style.display = "none"; + + + + var _self = this; + if (this.oDrag) { + this.oDrag.host = this; + this.oDrag.onmousedown = function(e){ + if (!e) e = event; + + //because of some issue I don't understand oExt.onmousedown is not called + if (!_self.$isWidget && (!_self.aData || !_self.dockable || _self.aData.hidden == 3)) + apf.WinServer.setTop(_self); + + if (_self.$lastState.maximized) + return false; + + + if (_self.aData && _self.dockable) { + if (_self.$lastState.normal) //@todo + _self.startDocking(e); + return false; + } + + }; + } + + this.$ext.onmousedown = function(){ + + var p = apf.document.activeElement; + if (p && p.$focusParent != _self && p.$focusParent.modal) + return false; + + + //Set ZIndex on oExt mousedown + if (!_self.$isWidget && (!_self.aData || !_self.dockable || _self.aData.hidden == 3)) + apf.WinServer.setTop(_self); + + if (!_self.$lastState.normal) + return false; + } + this.$ext.onmousemove = function(){ + if (!_self.$lastState.normal) + return false; + } + + + + /*var v; + if (!((v = this.getAttribute("visible")).indexOf("{") > -1 || v.indexOf("[") > -1)) { + this.$aml.setAttribute("visible", "{" + apf.isTrue(v) + "}"); + }*/ + }; + + this.$loadAml = function(x){ + apf.WinServer.setTop(this); + + this.$int = this.$getLayoutNode("main", "container", this.$ext); + + + if (this.oTitle) { + var _self = this; + (this.oTitle.nodeType != 1 + ? this.oTitle.parentNode + : this.oTitle).ondblclick = function(e){ + if (_self.state.indexOf("normal") == -1) + _self.restore(); + else if (_self.buttons.indexOf("max") > -1) + _self.maximize(); + else if (_self.buttons.indexOf("min") > -1) + _self.minimize(); + } + } + + if (typeof this.draggable == "undefined") { + (this.$propHandlers.draggable + || apf.GuiElement.propHandlers.draggable).call(this, true); + this.draggable = true; + } + + if (typeof this.buttons == "undefined") + this.buttons = ""; + //this.setProperty("buttons", "min|max|close"); + + + if (this.modal === undefined) { + this.$propHandlers.modal.call(this, true); + this.modal = true; + } + + //Set default visible hidden + if (!this.visible) { + this.$ext.style.display = "none"; + } + + else if (this.modal) { + var _self = this; + apf.queue.add("focus", function(){ + _self.focus(false, {mouse:true}); + }); + } + + + if (this.minwidth === undefined) + this.minwidth = this.$getOption("Main", "min-width"); + if (this.minheight === undefined) + this.minheight = this.$getOption("Main", "min-height"); + if (this.maxwidth === undefined) + this.maxwidth = this.$getOption("Main", "max-width"); + if (this.maxheight === undefined) + this.maxheight = this.$getOption("Main", "max-height"); + + if (this.center && this.visible) { + this.visible = false; + this.$ext.style.display = "none"; /* @todo temp done for project */ + + var _self = this; + $setTimeout(function(){ + _self.setProperty("visible", true); + }); + } + }; + + + this.$getActiveElements = function() { + // init $activeElements + if (!this.$activeElements) { + this.$activeElements = { + oTitle : this.oTitle, + oIcon : this.oIcon, + oDrag : this.oDrag + } + } + + return this.$activeElements; + } + + + + this.addEventListener("$skinchange", function(){ + if (this.title) + this.$propHandlers["title"].call(this, this.title); + + if (this.icon) + this.$propHandlers["icon"].call(this, this.icon); + }); + + + this.$destroy = function(skinChange){ + if (this.oDrag) { + this.oDrag.host = null; + this.oDrag.onmousedown = null; + apf.destroyHtmlNode(this.oDrag); + this.oDrag = null; + } + + this.oTitle = this.oIcon = null; + + if (this.$ext && !skinChange) { + this.$ext.onmousedown = null; + this.$ext.onmousemove = null; + } + }; +}).call(apf.modalwindow.prototype = new apf.Presentation()); + +apf.AmlWindow.prototype = apf.toolwindow.prototype = apf.modalwindow.prototype; + +apf.aml.setElement("toolwindow", apf.toolwindow); +apf.aml.setElement("modalwindow", apf.modalwindow); +apf.aml.setElement("window", apf.modalwindow); + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/elements/model.js)SIZE(42509)TIME(Tue, 13 Dec 2011 13:33:58 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + + +/** + * Element functioning as the central access point for xml data. Data can be + * retrieved from any data source using data instructions. Data can be + * submitted using data instructions in a similar way to html form posts. The + * modal can be reset to it's original state. It has support for offline use and + * {@link element.remove synchronization between multiple clients}. + * Example: + * + * + * + * Example: + * A small form where the bound data is submitted to a server using a model. + * + * + * + * + * + * + * Name + * + * Address + * + * Submit + * + * + * + * @event beforeretrieve Fires before a request is made to retrieve data. + * cancelable: Prevents the data from being retrieved. + * @event afterretrieve Fires when the request to retrieve data returns both on success and failure. + * @event receive Fires when data is successfully retrieved + * object: + * {String} data the retrieved data + * @event beforeload Fires before data is loaded into the model. + * cancelable: + * @event afterload Fires after data is loaded into the model. + * @event beforesubmit Fires before data is submitted. + * cancelable: Prevents the submit. + * object: + * {String} instruction The data instruction used to store the data. + * @event submiterror Fires when submitting data has failed. + * @event submitsuccess Fires when submitting data was successfull. + * @event aftersubmit Fires after submitting data. + * @event error Fires when a communication error has occured while making a request for this element. + * cancelable: Prevents the error from being thrown. + * bubbles: + * object: + * {Error} error the error object that is thrown when the event callback doesn't return false. + * {Number} state the state of the call + * Possible values: + * apf.SUCCESS the request was successfull + * apf.TIMEOUT the request has timed out. + * apf.ERROR an error has occurred while making the request. + * apf.OFFLINE the request was made while the application was offline. + * {mixed} userdata data that the caller wanted to be available in the callback of the http request. + * {XMLHttpRequest} http the object that executed the actual http request. + * {String} url the url that was requested. + * {Http} tpModule the teleport module that is making the request. + * {Number} id the id of the request. + * {String} message the error message. + * + * @constructor + * @define model + * @allowchild [cdata], instance, load, submission + * @addnode smartbinding, global + * @attribute {String} src the data instruction on how to load data from the data source into this model. + * @attribute {String} submission the data instruction on how to record the data from the data source from this model. + * @attribute {String} session the data instruction on how to store the session data from this model. + * @attribute {Boolean} autoinit whether to initialize the model immediately. If set to false you are expected to call init() when needed. This is useful when the system has to log in first. + * @attribute {Boolean} enablereset whether to save the original state of the data. This enables the use of the reset() call. + * @attribute {String} remote the id of the {@link element.remote} element to use for data synchronization between multiple clients. + * + * @author Ruben Daniels (ruben AT ajax DOT org) + * @version %I%, %G% + * @since 0.8 + */ +apf.model = function(struct, tagName){ + this.$init(tagName || "model", apf.NODE_HIDDEN, struct); + + this.$amlNodes = {}; + this.$propBinds = {}; + + this.$listeners = {}; + this.$proplisteners = {}; + + if (!apf.globalModel) { + apf.globalModel = this; + + apf.nameserver.register("model", "@default", this); + + } +}; + +(function(){ + this.$parsePrio = "020"; + this.$isModel = true; + + this.canHaveChildren = false; + this.enablereset = false; + + this.$state = 0;//1 = loading + + //1 = force no bind rule, 2 = force bind rule + this.$attrExcludePropBind = apf.extend({ + submission : 1, + src : 1, + session : 1 + }, this.$attrExcludePropBind); + + this.$booleanProperties["whitespace"] = true; + this.$booleanProperties["autoinit"] = true; + this.$booleanProperties.enablereset = true; + this.$supportedProperties = ["submission", "src", "session", "autoinit", + "enablereset", "remote", "whitespace"]; + + this.$propHandlers["src"] = + this.$propHandlers["get"] = function(value, prop){ + if (this.$amlLoaded) + this.$loadFrom(value); + }; + + + + /** + * Registers a aml element to this model in order for the aml element to + * receive data loaded in this model. + * + * @param {AMLElement} amlNode The aml element to be registered. + * @param {String} [xpath] the xpath query which is executed on the + * data of the model to select the node to be + * loaded in the amlNode. + * @return {Model} this model + * @private + */ + this.register = function(amlNode, xpath){ + if (!amlNode || !amlNode.load) //hasFeature(apf.__DATABINDING__)) + return this; + + var isReloading = amlNode.$model == this; + + //Remove previous model + if (amlNode.$model && !isReloading) + amlNode.$model.unregister(amlNode); + + //Register the aml node + var item = this.$amlNodes[amlNode.$uniqueId] = { + amlNode : amlNode, + xpath : xpath + }; + amlNode.$model = this; + + if (typeof amlNode.noloading == "undefined" + && amlNode.$setInheritedAttribute + && !amlNode.$setInheritedAttribute("noloading")) + amlNode.noloading = false; + + //amlNode.$model = this; + if (this.$state == 1) { + if (amlNode.clear && !amlNode.noloading) + amlNode.clear("loading");//@todo apf3.0 + } + else if (this.data) { + this.$loadInAmlNode(item); + //this.$loadInAmlProp(amlNode); + } + else { //@experimental + if (amlNode.hasFeature(apf.__CACHE__)) // amlNode.clear + amlNode.clear("empty"); + } + + var p, node, list = amlNode.$propsUsingMainModel, id = amlNode.$uniqueId; + for (var prop in list) { + this.$unbindXmlProperty(amlNode, prop); + p = this.$bindXmlProperty(amlNode, prop, + list[prop].xpath, list[prop].optimize); + + if (this.data) { + //if (node = p.root || p.listen ? this.data.selectSingleNode(p.root || p.listen) : this.data) { + if (node = p.listen ? this.data.selectSingleNode(p.listen) : this.data) { + amlNode.$execProperty(prop, node); + } + else + this.$waitForXml(amlNode, prop); + } + } + + return this; + }; + + this.$register = function(amlNode, xpath){ + //@todo apf3.0 update this.$propBinds; + + this.$amlNodes[amlNode.$uniqueId].xpath = xpath; + }; + + /** + * Removes a aml element from the group of registered aml elements. + * The aml element will not receive any updates from this model, however + * the data loaded in the aml element is not unloaded. + * + * @param {AMLElement} amlNode The aml element to be unregistered. + * @private + */ + this.unregister = function(amlNode){ + delete this.$amlNodes[amlNode.$uniqueId]; + + var list = amlNode.$propsUsingMainModel; + for (var prop in list) + this.$unbindXmlProperty(amlNode, prop); + + amlNode.dispatchEvent("unloadmodel"); + }; + + /** + * @private + */ + this.getXpathByAmlNode = function(amlNode){ + var n = this.$amlNodes[amlNode.$uniqueId]; + if (!n) + return false; + + return n.xpath; + }; + + /** + * @private + */ + this.$loadInAmlNode = function(item){ + var xmlNode; + var xpath = item.xpath; + var amlNode = item.amlNode; + + if (this.data && xpath) { + xmlNode = this.data.selectSingleNode(xpath); + } + else + xmlNode = this.data || null; + + if (xmlNode) { + delete this.$listeners[amlNode.$uniqueId]; + if (amlNode.xmlRoot != xmlNode) + amlNode.load(xmlNode); + } + else + this.$waitForXml(amlNode); + }; + + this.$loadInAmlProp = function(id, xmlNode){ + var prop, node, p = this.$propBinds[id], amlNode = apf.all[id]; + if (!amlNode){ + delete this.$propBinds[id]; + return; + } + + if (amlNode.$noInitModel) { + delete amlNode.$noInitModel; + return; + } + + + for (prop in p) { + if (xmlNode && (node = p[prop].listen + ? xmlNode.selectSingleNode(p[prop].listen) + : xmlNode)) { + apf.xmldb.addNodeListener(xmlNode, amlNode, + "p|" + id + "|" + prop + "|" + this.$uniqueId); + + delete this.$proplisteners[id + prop]; + amlNode.$execProperty(prop, node); + } + else + this.$waitForXml(amlNode, prop); + } + }; + + /* + We don't want to connect to the root, that would create a rush + of unnecessary update messages, so we'll find the element that's + closest to the node that is going to feed us the value + + mdlBlah::bli/persons + mdlBlah::bli/persons/person + + $attrBindings + //split / join, pop, indexOf + + + */ + this.$bindXmlProperty = function(amlNode, prop, xpath, optimize, listenRoot) { + var q ,p, id = amlNode.$uniqueId; + if (!this.$propBinds[id]) + this.$propBinds[id] = {}; + + /* + Store + 0 - Original xpath + 1 - Store point of change listener + 2 - Xpath to determine data node passed into load + */ + p = this.$propBinds[id][prop] = { + bind: xpath + }; + + //@todo apf3.0 + //Optimize root point, doesnt work right now because it doesnt change the original rule + if (optimize && false) { + //Find xpath for bind on this model of the amlNode + if ((q = this.$amlNodes[id]) && q.xpath) + xpath = (p.root = q.xpath) + "/" + xpath; + + var l = xpath.split("/"), z = l.pop(); + if (z.indexOf("@") == 0 + || z.indexOf("text()") > -1 + || z.indexOf("node()") > -1) { + p.listen = l.join("/"); + } + else p.listen = xpath; + } + else { + if ((q = this.$amlNodes[id]) && q.xpath) + p.listen = q.xpath; + } + + if (listenRoot) + p.listen = "."; + + if (this.data) { + var xmlNode = + + amlNode.$noInitModel ? amlNode.xmlRoot : + + (p.listen ? this.data.selectSingleNode(p.listen) : this.data); + + if (xmlNode) { + apf.xmldb.addNodeListener(xmlNode, amlNode, + "p|" + amlNode.$uniqueId + "|" + prop + "|" + this.$uniqueId); + + return p; + } + } + + this.$waitForXml(amlNode, prop); + + return p; + }; + + this.$unbindXmlProperty = function(amlNode, prop){ + var id = amlNode.$uniqueId; + + //@todo apf3.0 + var p = this.$propBinds[id] && this.$propBinds[id][prop]; + if (!p) return; + + if (this.data) { + var xmlNode = p.listen ? this.data.selectSingleNode(p.listen) : this.data; + if (xmlNode) { + apf.xmldb.removeNodeListener(xmlNode, amlNode, + "p|" + id + "|" + prop + "|" + this.$uniqueId); + } + } + + delete this.$proplisteners[id + prop]; + delete this.$propBinds[id][prop]; + return p; + }; + + /** + * Gets a copy of current state of the xml of this model. + * + * @return {XMLNode} context of this model + */ + this.getXml = function(){ + return this.data + ? apf.xmldb.cleanNode(this.data.cloneNode(true)) + : false; + }; + + /** + * Sets a value of an XMLNode based on an xpath statement executed on the data of this model. + * + * @param {String} xpath the xpath used to select a XMLNode. + * @param {String} value the value to set. + * @return {XMLNode} the changed XMLNode + */ + this.setQueryValue = function(xpath, value){ + if (!this.data) + return false; + + var node = apf.createNodeFromXpath(this.data, xpath); + if (!node) + return null; + + apf.setNodeValue(node, value, true); + //apf.xmldb.setTextNode(node, value); + return node; + }; + + /** + * Sets a value of a set of xml nodes based on an xpath statement executed on the data of this model. + * + * @param {String} xpath the xpath used to select a the nodeset. + * @param {String} value the value to set. + * @return {XMLNodeSet} the changed XMLNodeSet + */ + this.setQueryValues = function(xpath, value){ + if (!this.data) + return []; + + var nodes = this.data.selectNodes(xpath); + for (var i = 0, l = nodes.length; i < l; i++) + apf.setNodeValue(node, value, true); + + return nodes; + }; + + /** + * Gets the value of an XMLNode based on a xpath statement executed on the data of this model. + * + * @param {String} xpath the xpath used to select a XMLNode. + * @return {String} value of the XMLNode + */ + this.queryValue = function(xpath){ + if (!this.data) + return false; + + return apf.queryValue(this.data, xpath); + }; + + /** + * Gets the value of an XMLNode based on a xpath statement executed on the data of this model. + * + * @param {String} xpath the xpath used to select a XMLNode. + * @return {String} value of the XMLNode + */ + this.queryValues = function(xpath){ + if (!this.data) + return []; + + return apf.queryValue(this.data, xpath); + }; + + /** + * Executes an xpath statement on the data of this model + * + * @param {String} xpath the xpath used to select the XMLNode(s). + * @return {variant} XMLNode or NodeList with the result of the selection + */ + this.queryNode = function(xpath){ + if (!this.data) + return null; + + return this.data.selectSingleNode(xpath) + }; + + /** + * Executes an xpath statement on the data of this model + * + * @param {String} xpath the xpath used to select the XMLNode(s). + * @return {variant} XMLNode or NodeList with the result of the selection + */ + this.queryNodes = function(xpath){ + if (!this.data) + return []; + + return this.data.selectNodes(xpath); + }; + + /** + * Appends a copy of the xmlNode or model to this model as a child + * of it's root node + */ + this.appendXml = function(xmlNode, xpath){ + var insertNode = xpath + ? apf.createNodeFromXpath(this.data, xpath) + : this.data; + if (!insertNode) + return null; + + if (typeof xmlNode == "string") + xmlNode = apf.getXml(xmlNode); + else if (xmlNode.nodeFunc) + xmlNode = xmlNode.getXml(); + + if (!xmlNode) return; + + xmlNode = apf.xmldb.appendChild(insertNode, xmlNode); + + this.dispatchEvent("update", {xmlNode: xmlNode}); + return xmlNode; + }; + + /** + * Removes xmlNode from this model + */ + this.removeXml = function(xmlNode){ + if (typeof xmlNode == "string") + var xmlNodes = this.data.selectNodes(xmlNode); + else if (!xmlNode.length) + xmlNodes = [xmlNode]; + + if (xmlNodes.length) + apf.xmldb.removeNodeList(xmlNodes); + }; + + /** + * Clears the loaded data from this model. + */ + this.clear = function(){ + this.load(null); + doc = null; //Fix for safari refcount issue; + }; + + /** + * Resets data in this model to the last saved point. + * + */ + this.reset = function(){ + var doc = this.data.ownerDocument; + //doc.removeChild(this.data); + //var node = doc.appendChild(apf.isWebkit ? doc.importNode(this.$copy, true) : this.$copy); + this.data.parentNode.replaceChild(this.$copy, this.data); + this.load(this.$copy); + }; + + /** + * Sets a new saved point based on the current state of the data in this + * model. The reset() method returns the model to this point. + */ + this.savePoint = function(){ + this.$copy = apf.xmldb.getCleanCopy(this.data); + }; + + /** + * @private + */ + this.reloadAmlNode = function(uniqueId){ + if (!this.data) + return; + + var item = this.$amlNodes[uniqueId]; + var xmlNode = item.xpath + ? this.data.selectSingleNode(item.xpath) + : this.data; + item.amlNode.load(xmlNode); + }; + + /** + * @private + */ + //@todo refactor this to use .blah instead of getAttribute + //@todo move this to propHandlers + this.addEventListener("DOMNodeInsertedIntoDocument", function(e){ + var x = this.$aml; + if (this.parentNode && this.parentNode.hasFeature(apf.__DATABINDING__)) { + if (!this.name) + this.setProperty("id", "model" + this.parentNode.$uniqueId); + //this.parentNode.$aml.setAttribute("model", this.name); //@todo don't think this is necesary anymore... + this.register(this.parentNode); + } + + //Load literal model + if (!this.src) { + var strXml, xmlNode = x; + if (xmlNode && xmlNode.childNodes.length) { + if (apf.getNode(xmlNode, [0])) { + if ((strXml = xmlNode.xml || xmlNode.serialize()).match(/^[\s\S]*?>([\s\S]*)<[\s\S]*?$/)) { + strXml = RegExp.$1; //@todo apf3.0 test this with json + if (!apf.supportNamespaces) + strXml = strXml.replace(/xmlns=\"[^"]*\"/g, ""); + } + + if (this.whitespace === false) + strXml = strXml.replace(/>[\s\n\r]*<"); + + return this.load(apf.getXmlDom(strXml).documentElement); + } + // we also support JSON data loading in a model CDATA section + else if (apf.isJson(xmlNode.childNodes[0].nodeValue)) { + return this.load(apf.getXmlDom(xmlNode.childNodes[0].nodeValue).documentElement); + } + } + + //Default data for XForms models without an instance but with a submission node + if (this.submission) + this.load(""); + } + + //Load data into model if allowed + if (!apf.isFalse(this.autoinit)) + this.init(); + + //@todo actions apf3.0 + + return this; + }); + + /** + * Loads the initial data into this model. + * @see element.model.attribute.init + */ + //callback here is private + this.init = function(callback){ + if (this.session) { + this.$loadFrom(this.session, {isSession: true}); + } + else { + + + if (this.src) + this.$loadFrom(this.src, {callback: callback}); + } + }; + + /* *********** LOADING ****************/ + + /** + * Loads data into this model using a data instruction. + * @param {String} instruction the data instrution how to retrieve the data. + * @param {Object} options + * Properties: + * {XMLElement} xmlNode the {@link term.datanode data node} that provides context to the data instruction. + * {Function} callback the code executed when the data request returns. + * {mixed} [] Custom properties available in the data instruction. + */ + this.$loadFrom = function(instruction, options){ + + var data = instruction.split(":"); + + if (!options) + options = {}; + + if (!options.isSession) { + this.src = instruction; + this.$srcOptions = [instruction, options]; + } + + //Loading data in non-literal model + this.dispatchEvent("beforeretrieve"); + + //Set all components on loading... + var uniqueId, item; + for (uniqueId in this.$amlNodes) { + if (!(item = this.$amlNodes[uniqueId]) || !item.amlNode) + continue; + + //@todo apf3.0 + if (!item.amlNode.noloading) + item.amlNode.clear("loading"); + } + + this.$state = 1; + if (!this.$callCount) + this.$callCount = 1; + else + this.$callCount++; + + var _self = this, + callCount = this.$callCount, + callback = options.callback; + options.callback = function(data, state, extra){ + if (callCount != _self.$callCount) + return; //another call has invalidated this one + + _self.dispatchEvent("afterretrieve"); + + + + if (state != apf.SUCCESS) { + var oError; + + oError = new Error(apf.formatErrorString(1032, + _self, "Loading xml data", "Could not load data\n" + + "Instruction: " + instruction + "\n" + + "Url: " + extra.url + "\n" + + "Info: " + extra.message + "\n\n" + data)); + + if (callback && callback.apply(this, arguments) === true) + return true; + + if (extra.tpModule && extra.tpModule.retryTimeout(extra, state, _self, oError) === true) + return true; + + _self.$state = 0; + + throw oError; + } + + if (options && options.isSession && !data) { + if (this.src) + return _self.$loadFrom(this.src); + } + else { + if (options && options.cancel) + return; + + _self.load(data); + _self.dispatchEvent("receive", { + data: data + }); + + if (callback) + callback.apply(this, arguments); + } + }; + + return apf.getData(instruction, options); + }; + + /** + * Loads the data from the datasource specified for init. + */ + this.reload = function(){ + if (!this.data) + return; + + if (this.$srcOptions) + this.$loadFrom.apply(this, this.$srcOptions); + else if (this.src) + this.$loadFrom(this.src); + }; + + /** + * Loads data in this model + * + * @param {mixed} [xmlNode] the data to load in this model. A string specifies the data instruction how to retrieve the data, which can be an xml string. null will clear the data from this model. + * @param {Object} options + * Properties: + * {XMLElement} xmlNode the {@link term.datanode data node} that provides context to the data instruction. + * {Function} callback the code executed when the data request returns. + * {mixed} [] Custom properties available in the data instruction. + * {Boolean} [nocopy] Whether the data loaded will not overwrite the reset point. + */ + this.load = function(xmlNode, options){ + if (typeof xmlNode == "string") { + if (xmlNode.charAt(0) == "<") { //xml + if (xmlNode.substr(0, 5).toUpperCase() == "")+1); + if (!apf.supportNamespaces) + xmlNode = xmlNode.replace(/xmlns\=\"[^"]*\"/g, ""); + xmlNode = apf.getXmlDom(xmlNode, null, true).documentElement; //@todo apf3.0 whitespace issue + } + + else if (apf.isJson(xmlNode)) { + xmlNode = apf.json2Xml(xmlNode).documentElement + } + + else + return this.$loadFrom(xmlNode, options); + } + + if (this.ownerDocument && this.ownerDocument.$domParser.$shouldWait) { + //if (!this.$queueLoading) { + var _self = this; + this.data = xmlNode; //@todo expirement //this.$copy = + apf.xmldb.getXmlDocId(xmlNode, this); //@todo experiment + + this.$queueLoading = true; + apf.queue.add("modelload" + this.$uniqueId, function(){ + if (_self.ownerDocument && _self.ownerDocument.$domParser.$shouldWait) + apf.queue.add("modelload" + _self.$uniqueId, arguments.callee); + else { + _self.load(xmlNode, options); + _self.$queueLoading = false; + } + }); + //} + return; + } + else if (this.$queueLoading) + apf.queue.remove("modelload" + this.$uniqueId); + + this.$state = 0; + + if (this.dispatchEvent("beforeload", {xmlNode: xmlNode}) === false) + return false; + + var doc = xmlNode ? xmlNode.ownerDocument : null; //Fix for safari refcount issue; + + //if (apf.isIE && this.$aml && this.getAttribute("ns")) + //doc.setProperty("SelectionNamespaces", this.getAttribute("ns")); + + if (xmlNode) { + if (!apf.supportNamespaces) { + /* && (xmlNode.prefix || xmlNode.scopeName)) { + doc.setProperty("SelectionNamespaces", "xmlns:" + + (xmlNode.prefix || xmlNode.scopeName) + "='" + + xmlNode.namespaceURI + "'");*/ + var xmlns = [], attr = xmlNode.attributes; + for (var i = 0, l = attr.length; i < l; i++) { + if (attr[i].nodeName.substr(0, 5) == "xmlns") { + xmlns.push(attr[i].xml); + } + } + if (xmlns.length) + doc.setProperty("SelectionNamespaces", xmlns.join(" ")); + } + + apf.xmldb.addNodeListener(xmlNode, this); //@todo this one can be added for this.$listeners and when there are none removed + apf.xmldb.nodeConnect( + apf.xmldb.getXmlDocId(xmlNode, this), xmlNode, null, this); + + if ((!options || !options.nocopy) && this.enablereset) + this.$copy = apf.xmldb.getCleanCopy(xmlNode); + } + + this.data = xmlNode; + + this.dispatchEvent("afterload", {xmlNode: xmlNode}); + this.dispatchEvent("update", {xmlNode: xmlNode}); + + for (var id in this.$amlNodes) + this.$loadInAmlNode(this.$amlNodes[id]); + + for (id in this.$propBinds) + this.$loadInAmlProp(id, xmlNode); + + return this; + }; + + //Listening nodes should be removed in unregister + this.$waitForXml = function(amlNode, prop){ + if (prop) + this.$proplisteners[amlNode.$uniqueId + prop] = { + id : amlNode.$uniqueId, + amlNode : amlNode, + prop : prop + }; + else + this.$listeners[amlNode.$uniqueId] = amlNode; + + //When data is not available at model load but element had already data + //loaded, it is cleared here. + if (amlNode.xmlRoot) + amlNode.clear(); + }; + + this.$xmlUpdate = function(action, xmlNode, listenNode, UndoObj){ + //@todo optimize by only doing this for add, sync etc actions + + if (action == "replacenode" && xmlNode == this.data.ownerDocument.documentElement) { + var _self = this; + $setTimeout(function(){ + _self.load(xmlNode); + }); + return; + } + + + + + if (apf.uirecorder && apf.uirecorder.captureDetails) { + if (apf.uirecorder.isLoaded && (apf.uirecorder.isRecording || apf.uirecorder.isTesting)) {// only capture events when recording + if (this.ownerDocument && this.$aml) { + apf.uirecorder.capture.captureModelChange({ + action : action, + amlNode : this, + xmlNode : xmlNode, + listenNode : listenNode, + UndoObj : UndoObj + }); + } + } + } + + + var p, b; + for (var id in this.$listeners) { + if (xmlNode = this.data.selectSingleNode(this.$amlNodes[id].xpath || ".")) { + this.$listeners[id].load(xmlNode); + delete this.$listeners[id]; + } + } + + for (id in this.$proplisteners) { + p = this.$proplisteners[id]; + b = this.$propBinds[p.id][p.prop]; + if (xmlNode = b.listen ? this.data.selectSingleNode(b.listen) : this.data) { + delete this.$proplisteners[id]; + + apf.xmldb.addNodeListener(xmlNode, p.amlNode, + "p|" + p.id + "|" + p.prop + "|" + this.$uniqueId); + + p.amlNode.$execProperty(p.prop, b.root + ? this.data.selectSingleNode(b.root) + : this.data); + } + } + + this.dispatchEvent("update", {xmlNode: xmlNode, action: action, undoObj: UndoObj}); + }; + + /**** INSERT ****/ + + /** + * Inserts data into the data of this model using a data instruction. + * @param {String} instruction the data instrution how to retrieve the data. + * @param {Object} options + * Properties: + * {XMLElement} insertPoint the parent element for the inserted data. + * {Boolean} clearContents wether the contents of the insertPoint should be cleared before inserting the new children. + * {Boolean} copyAttributes wether the attributes of the merged element are copied. + * {Function} callback the code executed when the data request returns. + * {mixed} <> Custom properties available in the data instruction. + */ + this.$insertFrom = function(instruction, options){ + if (!instruction) return false; + + this.dispatchEvent("beforeretrieve"); + + + + var callback = options.callback, _self = this; + options.callback = function(data, state, extra){ + _self.dispatchEvent("afterretrieve"); + + if (!extra) + extra = {}; + + if (state != apf.SUCCESS) { + var oError; + + + + if (extra.tpModule.retryTimeout(extra, state, + options.amlNode || _self, oError) === true) + return true; + + if (callback + && callback.call(this, extra.data, state, extra) === false) + return; + + throw oError; + } + + //Checking for xpath + if (typeof options.insertPoint == "string") + options.insertPoint = _self.data.selectSingleNode(options.insertPoint); + + if (typeof options.clearContents == "undefined" && extra.userdata) + options.clearContents = apf.isTrue(extra.userdata[1]); //@todo is this still used? + + if (options.whitespace == undefined) + options.whitespace = _self.whitespace; + + //Call insert function + (options.amlNode || _self).insert(data, options); + + if (callback) + callback.call(this, extra.data, state, extra); + }; + + apf.getData(instruction, options); + }; + + /** + * Inserts data in this model as a child of the currently loaded data. + * + * @param {XMLElement} XMLRoot the {@link term.datanode data node} to insert into this model. + * @param {Object} options + * Properties: + * {XMLElement} insertPoint the parent element for the inserted data. + * {Boolean} clearContents wether the contents of the insertPoint should be cleared before inserting the new children. + * {Boolean} copyAttributes wether the attributes of the merged element are copied. + * {Function} callback the code executed when the data request returns. + * {mixed} <> Custom properties available in the data instruction. + */ + this.insert = function(xmlNode, options){ + if (typeof xmlNode == "string") { + if (xmlNode.charAt(0) == "<") { + if (xmlNode.substr(0, 5).toUpperCase() == "")+1); + if (!apf.supportNamespaces) + xmlNode = xmlNode.replace(/xmlns\=\"[^"]*\"/g, ""); + + if (this.whitespace === false) + xmlNode = xmlNode.replace(/>[\s\n\r]*<"); + + xmlNode = apf.getXmlDom(xmlNode).documentElement; + } + + else if (apf.isJson(xmlNode)) { + xmlNode = apf.json2Xml(xmlNode).documentElement + } + + else + return this.$insertFrom(xmlNode, options); + } + + if (!options.insertPoint) + options.insertPoint = this.data; + + + + //if(this.dispatchEvent("beforeinsert", parentXMLNode) === false) return false; + + //Integrate XMLTree with parentNode + if (typeof options.copyAttributes == "undefined") + options.copyAttributes = true; + + var newNode = apf.mergeXml(xmlNode, options.insertPoint, options); + + //Call __XMLUpdate on all this.$listeners + apf.xmldb.applyChanges("insert", options.insertPoint);//parentXMLNode); + + //this.dispatchEvent("afterinsert"); + + return xmlNode; + }; + + /* *********** SUBMISSION ****************/ + + /** + * Serialize the full XML DOM to a format specified by 'type' + * + * @param {String} type how to serialize the data + */ + this.convertXml = function(type) { + if (!type) + return this.data.xml; + + return apf.convertXml(this.data, type); + }; + + /** + * Submit the data of the model to a data source. + * @param {String} instruction the instruction for sending the data, or the url to send the data to. + * @param {String} type how to serialize the data. + * Possible values: + * xml, application/xml + * form, application/x-www-form-urlencoded + * json, application/json + * @param {XMLElement} xmlNode the data node to send to the server. + */ + //@todo rewrite this for apf3.0 + this.submit = function(instruction, type, xmlNode, options){ + if (!instruction) + instruction = this.submission; + + if (!xmlNode) + xmlNode = this.data; + + + + if (!type) + type = "form"; + + if (this.dispatchEvent("beforesubmit", { + instruction: instruction + }) === false) + return false; + + var model = this; + function cbFunc(data, state, extra){ + if ((state == apf.TIMEOUT + || (model.retryOnError && state == apf.ERROR)) + && extra.retries < apf.maxHttpRetries) { + return extra.tpModule.retry(extra.id); + } + else { + if (state != apf.SUCCESS) { + model.dispatchEvent("submiterror", extra); + } + else { + model.dispatchEvent("submitsuccess", apf.extend({ + data: data + }, extra)); + } + } + } + + var data; + if (type.indexOf("xml") > -1) + data = apf.getXmlString(xmlNode); + else if (type.indexOf("form") > -1) + data = apf.convertXml(apf.xmldb.getCleanCopy(xmlNode), "cgiobjects"); + else if (type.indexOf("json") > -1) + data = apf.convertXml(xmlNode, "json"); + + apf.saveData(instruction, apf.extend({ + xmlNode : xmlNode, + data : data, + callback : cbFunc + }, options)); + + this.dispatchEvent("aftersubmit"); + }; + + this.$destroy = function(){ + if (this.session && this.data) + apf.saveData(this.session, {xmlNode: this.getXml()}); + }; +}).call(apf.model.prototype = new apf.AmlElement()); + +apf.aml.setElement("model", apf.model); + + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/elements/notifier.js)SIZE(15297)TIME(Wed, 19 Oct 2011 09:20:28 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + +/** + * Notification element, which shows popups when events occur. Similar + * to growl on the OSX platform. + * Example: + * + * + * + * + * + * + * + * + * Example: + * Notifier with 4 notifications which appears and stays over the 3 seconds + * begins to the top right corner and goes to the left. First notification will + * be displayed when value in textbox will be bigger than 4. In next two cases + * notification will be shown when notifier's position or arrange attribute will + * be changed. In the last case notification will be shown when date 2008-12-24 + * will be selected on calendar. + * + * + * + * + * + * + * + * + * + * @define notifier + * @attribute {String} position Vertical and horizontal element's start + * position, it can be changed in any time, + * default is 'top-right' + * Possible values: + * top-right element is placed in top-right corner of browser window + * top-left element is placed in top-left corner of browser window + * bottom-right element is placed in bottom-right corner of browser window + * bottom-left element is placed in bottom-left corner of browser window + * center-center element is placed in the middle of browser window + * right-top element is placed in top-right corner of browser window + * left-top element is placed in top-left corner of browser window + * right-bottom element is placed in bottom-right corner of browser window + * left-bottom element is placed in bottom-left corner of browser window + * center-center element is placed in the middle of browser window + * @attribute {String} margin It's a free space around popup element, + * default is '10 10 10 10' pixels + * @attribute {String} columnsize Specify element width and col width where + * element will be displayed, default is 300 pixels + * @attribute {String} arrange popup elements can be displayed in rows + * or columns, default is 'vertical' + * Possible values: + * vertical element will be displayed in rows + * horizontal element will be displayed in columns + * @attribute {String} timeout After the timeout has passed the popup + * will dissapear automatically. When the + * mouse hovers over the popup it doesn't + * dissapear, default is 2 seconds + * $attribute {String} onclick It's an action executed after user click + * on notifier cloud + * + * @constructor + * + * @inherits apf.Presentation + * + * @author + * @version %I%, %G% + * + * @allowchild event + */ +apf.notifier = function(struct, tagName){ + this.$init(tagName || "notifier", apf.NODE_VISIBLE, struct); +}; + +(function() { + this.timeout = 2000; + this.position = "top-right"; + this.columnsize = 300; + this.arrange = "vertical"; + this.margin = "10 10 10 10"; + + this.lastPos = null; + this.showing = 0; + this.sign = 1; + + this.$supportedProperties.push("margin", "position", "timeout", + "columnsize", "arrange"); + + this.$propHandlers["position"] = function(value) { + this.lastPos = null; + }; + + this.$propHandlers["margin"] = function(value) { + this.margin = value; + }; + + this.$propHandlers["timeout"] = function(value) { + this.timeout = parseInt(value) * 1000; + }; + + function getPageScroll() { + return [ + document.documentElement.scrollTop || document.body.scrollTop, + document.documentElement.scrollLeft || document.body.scrollLeft + ]; + } + + function getStartPosition(x, wh, ww, nh, nw, margin) { + var scrolled = getPageScroll(); + + return [ + (x[0] == "top" + ? margin[0] + : (x[0] == "bottom" + ? wh - nh - margin[2] + : wh / 2 - nh / 2)) + scrolled[0], + (x[1] == "left" + ? margin[3] + : (x[1] == "right" + ? ww - nw - margin[1] + : ww / 2 - nw / 2)) + scrolled[1] + ]; + } + + /** + * Function creates new notifie popup element + * + * @param {String} message Message content displaing in popup element, + * default is [No message] + * @param {String} icon Path to icon file relative to "icon-path" which + * is set in skin declaration + * @param {Object} ev object representation of event + * + */ + this.popup = function(message, icon, ev) { + if (!this.$ext) + return; + + this.$ext.style.width = this.columnsize + "px"; + + var _self = this, + oNoti = this.$pHtmlNode.appendChild(this.$ext.cloneNode(true)), + ww = apf.isIE + ? document.documentElement.offsetWidth + : window.innerWidth, + wh = apf.isIE + ? document.documentElement.offsetHeight + : window.innerHeight, + + removed = false, + + oIcon = this.$getLayoutNode("notification", "icon", oNoti), + oBody = this.$getLayoutNode("notification", "body", oNoti); + + this.showing++; + + if (oIcon && icon) { + if (oIcon.nodeType == 1) { + oIcon.style.backgroundImage = "url(" + + this.iconPath + icon + ")"; + } + else { + oIcon.nodeValue = this.iconPath + icon; + } + + this.$setStyleClass(oNoti, this.$baseCSSname + "ShowIcon"); + } + + oBody.insertAdjacentHTML("beforeend", message || "[No message]"); + oNoti.style.display = "block"; + + var margin = apf.getBox(this.margin || "0"), + nh = oNoti.offsetHeight, + nw = oNoti.offsetWidth, + /* It's possible to set for example: position: top-right or right-top */ + x = this.position.split("-"), + _reset = false; + + if (x[1] == "top" || x[1] == "bottom" || x[0] == "left" || x[0] == "right") + x = [x[1], x[0]]; + /* center-X and X-center are disabled */ + if ((x[0] == "center" && x[1] !== "center") || (x[0] !== "center" && x[1] == "center")) + x = ["top", "right"]; + + /* start positions */ + if (!this.lastPos) { + this.lastPos = getStartPosition(x, wh, ww, nh, nw, margin); + this.sign = 1; + _reset = true; + } + + if ((!_reset && x[0] == "bottom" && this.sign == 1) || + (x[0] == "top" && this.sign == -1)) { + if (this.arrange == "vertical") { + this.lastPos[0] += x[1] == "center" + ? 0 + : this.sign * (x[0] == "top" + ? margin[0] + nh + : (x[0] == "bottom" + ? - margin[2] - nh + : 0)); + } + else { + this.lastPos[1] += x[0] == "center" + ? 0 + : this.sign * (x[1] == "left" + ? margin[3] + nw + : (x[1] == "right" + ? - margin[1] - nw + : 0)); + } + } + + /* reset to next line, first for vertical, second horizontal */ + var scrolled = getPageScroll(); + + if (this.lastPos[0] > wh + scrolled[0] - nh || this.lastPos[0] < scrolled[0]) { + this.lastPos[1] += (x[1] == "left" + ? nw + margin[3] + : (x[1] == "right" + ? - nw - margin[3] + : 0)); + this.sign *= -1; + this.lastPos[0] += this.sign*(x[0] == "top" + ? margin[0] + nh + : (x[0] == "bottom" + ? - margin[2] - nh + : 0)); + } + else if (this.lastPos[1] > ww + scrolled[1] - nw || this.lastPos[1] < scrolled[1]) { + this.lastPos[0] += (x[0] == "top" + ? nh + margin[0] + : (x[0] == "bottom" + ? - nh - margin[0] + : 0)); + this.sign *= -1; + this.lastPos[1] += x[0] == "center" + ? 0 + : this.sign * (x[1] == "left" + ? margin[3] + nw + : (x[1] == "right" + ? - margin[1] - nw + : 0)); + } + + /* Start from begining if entire screen is filled */ + if (this.lastPos) { + if ((this.lastPos[0] > wh + scrolled[0] - nh || this.lastPos[0] < scrolled[1]) + && this.arrange == "horizontal") { + this.lastPos = getStartPosition(x, wh, ww, nh, nw, margin); + this.sign = 1; + } + if ((this.lastPos[1] > ww + scrolled[1] - nw || this.lastPos[1] < scrolled[1]) + && this.arrange == "vertical") { + this.lastPos = getStartPosition(x, wh, ww, nh, nw, margin); + this.sign = 1; + } + } + + oNoti.style.left = this.lastPos[1] + "px"; + oNoti.style.top = this.lastPos[0] + "px"; + + if ((x[0] == "top" && this.sign == 1) || (x[0] == "bottom" && this.sign == -1)) { + if (this.arrange == "vertical") { + this.lastPos[0] += x[1] == "center" + ? 0 + : this.sign * (x[0] == "top" + ? margin[0] + nh + : (x[0] == "bottom" + ? - margin[2] - nh + : 0)); + } + else { + this.lastPos[1] += x[0] == "center" + ? 0 + : this.sign * (x[1] == "left" + ? margin[3] + nw + : (x[1] == "right" + ? - margin[1] - nw + : 0)); + } + }; + + var isMouseOver = false; + + apf.tween.css(oNoti, "fade", { + anim : apf.tween.NORMAL, + steps : 10, + interval : 10, + onfinish : function(container) { + oNoti.style.filter = ""; + $setTimeout(hideWindow, _self.timeout) + } + }); + + function hideWindow() { + if (isMouseOver) + return; + + apf.tween.css(oNoti, "notifier_hidden", { + anim : apf.tween.NORMAL, + steps : 10, + interval: 20, + onfinish: function(container) { + apf.setStyleClass(oNoti, "", ["notifier_hover"]); + if (isMouseOver) + return; + + if (oNoti.parentNode) { + if (oNoti.parentNode.removeChild(oNoti) && !removed) { + _self.showing--; + removed = true; + } + } + + if (_self.showing == 0) + _self.lastPos = null; + } + }); + } + + /* Events */ + oNoti.onmouseover = function(e) { + e = (e || event); + var tEl = e.explicitOriginalTarget || e.toElement; + if (isMouseOver) + return; + if (tEl == oNoti || apf.isChildOf(oNoti, tEl)) { + apf.tween.css(oNoti, "notifier_hover", { + anim : apf.tween.NORMAL, + steps : 10, + interval: 20, + onfinish: function(container) { + apf.setStyleClass(oNoti, "", ["notifier_shown"]); + } + }); + + isMouseOver = true; + } + }; + + oNoti.onmouseout = function(e) { + e = (e || event); + var tEl = e.explicitOriginalTarget || e.toElement; + + if (!isMouseOver) + return; + + if (apf.isChildOf(tEl, oNoti) || + (!apf.isChildOf(oNoti, tEl) && oNoti !== tEl )) { + isMouseOver = false; + hideWindow(); + } + }; + + if (ev) { + oNoti.onclick = function() { + ev.dispatchEvent("click"); + }; + } + }; + + /**** Init ****/ + + this.$draw = function() { + //Build Main Skin + this.$pHtmlNode = document.body; + + this.$ext = this.$getExternal("notification"); + this.$ext.style.display = "none"; + this.$ext.style.position = "absolute"; + apf.window.zManager.set("notifier", this.$ext); + }; +}).call(apf.notifier.prototype = new apf.Presentation()); + +apf.aml.setElement("notifier", apf.notifier); +apf.aml.setElement("event", apf.event); + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/elements/page.js)SIZE(26692)TIME(Tue, 13 Dec 2011 13:42:34 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + + +/** + * A page in a pageable element. (i.e. a page in {@link element.tab}) + * + * @constructor + * @define page + * @allowchild {elements}, {anyaml} + * @addnode elements + * + * @author Ruben Daniels (ruben AT ajax DOT org) + * @version %I%, %G% + * @since 0.8 + */ +apf.page = function(struct, tagName){ + this.$init(tagName || "page", apf.NODE_VISIBLE, struct); +}; + +(function(){ + this.canHaveChildren = true; + + this.$focussable = false; + this.closebtn = false; + + + + + /** + * Sets the caption of the button of this element. + * @param {String} caption the text displayed on the button of this element. + */ + this.setCaption = function(caption){ + this.setProperty("caption", caption, false, true); + }; + + /** + * Sets the icon of the button of this element. + * @param {String} icon the icon displayed on the button of this element. + */ + this.setIcon = function(icon) { + this.setProperty("icon", icon, false, true); + }; + + + /**** Delayed Render Support ****/ + + + //Hack + this.addEventListener("beforerender", function(){ + this.parentNode.dispatchEvent("beforerender", { + page : this + }); + }); + + this.addEventListener("afterrender", function(){ + this.parentNode.dispatchEvent("afterrender", { + page : this + }); + }); + + + /**** Properties ****/ + + this.$booleanProperties["visible"] = true; + this.$booleanProperties["fake"] = true; + this.$booleanProperties["closebtn"] = true; + this.$supportedProperties.push("fake", "caption", "icon", "tooltip", + "type", "buttons", "closebtn", "trans-in", "trans-out"); + + + /** + * @attribute {Boolean} closebtn whether this page's button shows a close button inside it. + */ + this.$propHandlers["closebtn"] = function(value){ + //if (!this.$amlLoaded || !this.parentNode.$hasButtons) + // return; + var _self = this; + + if (value) { + var btncontainer = this.parentNode.$getLayoutNode("button", "container", this.$button); + + this.parentNode.$getNewContext("btnclose"); + var elBtnClose = this.parentNode.$getLayoutNode("btnclose"); + + if (elBtnClose) { + // if(elBtnClose.nodeType == 1) { + apf.setStyleClass(this.$button, "btnclose"); + + elBtnClose.addEventListener("mousedown", function(e){ + apf.cancelBubble(e, apf.lookup(_self.$uniqueId)); + }); + + elBtnClose.addEventListener("click", function(e){ + var page = apf.lookup(_self.$uniqueId); + page.parentNode.remove(page, e); + }); + + btncontainer.appendChild(elBtnClose); + } + + } + }; + + + /** + * @attribute {String} caption the text displayed on the button of this element. + */ + this.$propHandlers["tooltip"] = function(value){ + if (!this.parentNode) + return; + + var node = this.parentNode + .$getLayoutNode("button", "caption", this.$button); + + (node.nodeType == 1 ? node : node.parentNode).setAttribute("title", value || ""); + } + + /** + * @attribute {String} caption the text displayed on the button of this element. + */ + this.$propHandlers["caption"] = function(value){ + if (!this.parentNode) + return; + + var node = this.parentNode + .$getLayoutNode("button", "caption", this.$button); + + if (node.nodeType == 1) + node.innerHTML = value; + else + node.nodeValue = value; + }; + + this.$propHandlers["icon"] = function(value) { + if (!this.parentNode) + return; + + var node = this.parentNode + .$getLayoutNode("button", "icon", this.$button); + + if (node && node.nodeType == 1) + apf.skins.setIcon(node, value, this.parentNode.iconPath); + }; + + this.$propHandlers["visible"] = function(value){ + if (!this.parentNode) + return; + + if (value) { + if (this.$fake) { + this.parentNode.set(this.$fake); + this.visible = false; + return; + } + + this.$ext.style.display = ""; + if (this.parentNode.$hasButtons) + this.$button.style.display = "block"; + + if (!this.parentNode.$activepage) + this.parentNode.set(this); + } + else { + if (this.$active) { + this.$deactivate(); + + // Try to find a next page, if any. + var nextPage = this.parentNode.activepagenr + 1; + var pages = this.parentNode.getPages() + var len = pages.length + while (nextPage < len && !pages[nextPage].visible) + nextPage++; + + if (nextPage == len) { + // Try to find a previous page, if any. + nextPage = this.parentNode.activepagenr - 1; + while (nextPage >= 0 && len && !pages[nextPage].visible) + nextPage--; + } + + if (nextPage >= 0) + this.parentNode.set(nextPage); + else { + this.parentNode.activepage = + this.parentNode.activepagenr = + this.parentNode.$activepage = null; + } + } + + this.$ext.style.display = "none"; + if (this.parentNode.$hasButtons) + this.$button.style.display = "none"; + } + }; + + /** + * @attribute {Boolean} fake whether this page actually contains elements or + * only provides a button in the pageable parent element. + */ + this.$propHandlers["fake"] = function(value){ + if (this.$ext) { + apf.destroyHtmlNode(this.$ext); + this.$int = this.$ext = null; + } + }; + + this.$propHandlers["type"] = function(value) { + this.setProperty("fake", true); + + if (this.relPage && this.$active) + this.relPage.$deactivate(); + + this.relPage = this.parentNode.getPage(value); + if (this.$active) + this.$activate(); + }; + + /**** DOM Hooks ****/ + + this.addEventListener("DOMNodeRemoved", function(e){ + if (e && e.currentTarget != this) + return; + + if (this.$button) { + if (this.$position & 1) + this.parentNode.$setStyleClass(this.$button, "", ["firstbtn", "firstcurbtn"]); + if (this.$position & 2) + this.parentNode.$setStyleClass(this.$button, "", ["lastbtn"]); + } + + if (!e.$doOnlyAdmin) { + if (this.$button) + this.$button.parentNode.removeChild(this.$button); + + if (this.parentNode && this.parentNode.$activepage == this) { + if (this.$button) + this.parentNode.$setStyleClass(this.$button, "", ["curbtn"]); + this.parentNode.$setStyleClass(this.$ext, "", ["curpage"]); + } + } + }); + + //beforeNode, pNode, withinParent + this.addEventListener("DOMNodeInserted", function(e){ + if (e && e.currentTarget != this || !this.$amlLoaded) //|| !e.$oldParent + return; + + if (!e.$isMoveWithinParent + && this.skinName != this.parentNode.skinName) { + this.$destroy(); //clean up button + } + else if (this.$button && (!e.$oldParent || e.$oldParent.$hasButtons) && this.parentNode.$buttons) + this.parentNode.$buttons.insertBefore(this.$button, + e.$beforeNode && e.$beforeNode.$button || null); + }, true); + + /**** Private state functions ****/ + + this.$position = 0; + this.$first = function(remove){ + if (remove) { + this.$isFirst = false; + this.$position -= 1; + this.parentNode.$setStyleClass(this.$button, "", + ["firstbtn", "firstcurbtn"]); + } + else { + this.$isFirst = true; + this.$position = this.$position | 1; + this.parentNode.$setStyleClass(this.$button, "firstbtn" + + (this.parentNode.$activepage == this ? " firstcurbtn" : "")); + } + }; + + this.$last = function(remove){ + if (remove) { + this.$isLast = false; + this.$position -= 2; + this.parentNode.$setStyleClass(this.$button, "", ["lastbtn"]); + } + else { + this.$isLast = true; + this.$position = this.$position | 2; + this.parentNode.$setStyleClass(this.$button, "lastbtn"); + } + }; + + this.$deactivate = function(fakeOther){ + //if (this.disabled) + //return false; + + this.$active = false; + + if (this.parentNode.$hasButtons) { + if (this.$position > 0) + this.parentNode.$setStyleClass(this.$button, "", ["firstcurbtn"]); + this.parentNode.$setStyleClass(this.$button, "", ["curbtn"]); + } + + if ((!this.fake || this.relPage) && !fakeOther) { + this.parentNode.$setStyleClass(this.fake + ? this.relPage.$ext + : this.$ext, "", ["curpage"]); + + if (this.fake) { + if (!this.relPage.visible) + this.relPage.$ext.style.display = "none"; + + this.relPage.dispatchEvent("prop.visible", {value:false}); + } + + this.dispatchEvent("prop.visible", {value:false}); + } + }; + + this.$deactivateButton = function() { + if (this.parentNode && this.parentNode.$hasButtons) { + if (this.$position > 0) + this.parentNode.$setStyleClass(this.$button, "", ["firstcurbtn"]); + this.parentNode.$setStyleClass(this.$button, "", ["curbtn"]); + } + }; + + this.$activate = function(){ + //if (this.disabled) + //return false; + + this.$active = true; + + if (!this.$drawn) { + var f; + this.addEventListener("DOMNodeInsertedIntoDocument", f = function(e){ + this.removeEventListener("DOMNodeInsertedIntoDocument", f); + if (!this.$active) + return; + + this.$activate(); + }); + return; + } + + if (this.parentNode.$hasButtons) { + if (this.$isFirst) + this.parentNode.$setStyleClass(this.$button, "firstcurbtn"); + this.parentNode.$setStyleClass(this.$button, "curbtn"); + } + + if (!this.fake || this.relPage) { + if (this.fake) { + if (this.relPage) { + this.relPage.$ext.style.display = ""; + this.parentNode.$setStyleClass(this.relPage.$ext, "curpage"); + this.relPage.$fake = this; + + + if (this.relPage.$render) + this.relPage.$render(); + + + this.relPage.dispatchEvent("prop.visible", {value:true}); + } + } + else { + this.parentNode.$setStyleClass(this.$ext, "curpage"); + } + + + if (apf.layout && this.relPage) + apf.layout.forceResize(this.fake ? this.relPage.$int : this.$int); + + } + + + if (this.$render) + this.$render(); + + + if (!this.fake) { + if (apf.isIE) { + var cls = this.$ext.className; + this.$ext.className = "rnd" + Math.random(); + this.$ext.className = cls; + } + + this.dispatchEvent("prop.visible", {value:true}); + } + }; + + this.$activateButton = function() { + if (this.$active) + return; + + if (!this.$drawn) { + var f; + this.addEventListener("DOMNodeInsertedIntoDocument", f = function(e){ + this.removeEventListener("DOMNodeInsertedIntoDocument", f); + this.$activateButton(); + }); + return; + } + + if (this.parentNode && this.parentNode.$hasButtons) { + if (this.$isFirst) + this.parentNode.$setStyleClass(this.$button, "firstcurbtn"); + this.parentNode.$setStyleClass(this.$button, "curbtn"); + } + + + if (this.$render) + this.$render(); + + }; + + this.addEventListener("$skinchange", function(){ + if (this.caption) + this.$propHandlers["caption"].call(this, this.caption); + + if (this.icon) + this.$propHandlers["icon"].call(this, this.icon); + }); + + this.$enable = function(){ + if (this.$button) + this.$setStyleClass(this.$button, null, ["btnDisabled"]);//@todo this.$baseCSSname + + }; + + this.$disable = function(){ + if (this.$button) + this.$setStyleClass(this.$button, "btnDisabled");//@todo this.$baseCSSname + + }; + + function $btnSet(oHtml){ + this.parentNode.set(this); + this.canHaveChildren = 2; + this.$setStyleClass(oHtml, "down", null, true); + } + + this.$btnControl = {}; + this.$btnDown = function(oHtml, htmlEvent){ + if (this.disabled) + return; + + if (htmlEvent.button == 2 && this.parentNode.contextmenu) { + this.parentNode.contextPage = this; + return; + } + + if (this.parentNode.dispatchEvent("tabselectclick", { + page: this, + htmlEvent: htmlEvent + }) === false) + return; + + this.$btnPressed = true; + + if (!this.parentNode.$order) + $btnSet.call(this, oHtml); + + //@todo vertically stacked buttons + else if (this.parentNode.getPages().length > 1) { + if (this.$btnControl[this.$uniqueId]) + return; + + this.$dragging = true; + + var pos = apf.getAbsolutePosition(this.$button, this.parentNode.$ext); + var start = htmlEvent.clientX; + var x = start - pos[0]; + var t = apf.getAbsolutePosition(this.$button)[1]; + oHtml.style.left = (oHtml.offsetLeft) + "px"; + oHtml.style.top = (oHtml.offsetTop) + "px"; + oHtml.style.position = "absolute"; + + var div = document.createElement("div"); + div.style.width = oHtml.offsetWidth + "px"; + div.style.marginLeft = apf.getStyle(this.$button, "marginLeft"); + div.style.marginRight = apf.getStyle(this.$button, "marginRight"); + + this.$button.parentNode.insertBefore(div, this.$button); + + var marginWidth = Math.abs(apf.getMargin(div)[0]); + + var mUp, mMove, _self = this, started; + apf.addListener(document, "mousemove", mMove = function(e){ + if (!e) e = event; + + if (!started) { + if (Math.abs(start - e.clientX) < 3) + return; + started = true; + + oHtml.style.zIndex = 1; + } + + oHtml.style.left = "-2000px"; + + var el = document.elementFromPoint(e.clientX, t + 1); + var aml = el && apf.findHost(el); + + oHtml.style.left = (e.clientX - x) + "px"; + + if (aml && aml.localName == "page") { + aml.$button.style.position = "relative"; + + var obj, onRight = div.offsetLeft > aml.$button.offsetLeft; + + var pos = apf.getAbsolutePosition(aml.$button); + if (onRight && aml.$button.offsetWidth - e.clientX + pos[0] < marginWidth) + return; + + if (obj = _self.$btnControl[aml.$uniqueId]) { + if (obj.onRight != onRight) + obj.stop(); + else + return; + } + + _self.$btnControl[aml.$uniqueId] = {onRight: onRight}; + + var newPosition = _self.$lastPosition = onRight + ? aml.$button + : aml.$button.nextSibling; + _self.$lastLeft = aml.$button.offsetLeft; + + apf.tween.single(aml.$button, { + steps : 20, + interval: 10, + from : 0, + to : onRight + ? aml.$button.offsetWidth - marginWidth + : -1 * (aml.$button.offsetWidth - marginWidth), + type : "left", + anim : apf.tween.easeInOutCubic, + control : _self.$btnControl[aml.$uniqueId], + onstop : function(){ + + }, + onfinish : function(){ + aml.$button.style.left = + aml.$button.style.position = ""; + + delete _self.$btnControl[aml.$uniqueId]; + + if (div && div.parentNode) + div.parentNode.insertBefore(div, newPosition); + + _self.$lastPosition = + _self.$lastLeft = undefined; + } + }); + } + }); + + apf.addListener(document, "mouseup", mUp = function(e){ + if (!e) e = event; + + var aml = _self.$lastPosition !== null + ? apf.findHost(_self.$lastPosition || div.nextSibling) + : null; + if (started && aml != _self.nextSibling) { + apf.tween.single(_self.$button, { + steps : 20, + interval: 10, + from : _self.$button.offsetLeft, + to : _self.$lastLeft || div.offsetLeft, + type : "left", + control : _self.$btnControl[_self.$uniqueId] = {}, + anim : apf.tween.easeInOutCubic, + onstop : function(){ + + }, + onfinish : function(){ + oHtml.style.position = + oHtml.style.zIndex = + oHtml.style.top = + oHtml.style.left = ""; + + _self.parentNode.insertBefore(_self, aml); + div.parentNode.removeChild(div); + + delete _self.$btnControl[_self.$uniqueId]; + + _self.parentNode.dispatchEvent("tabselectmouseup"); + } + }); + } + else { + oHtml.style.position = + oHtml.style.zIndex = + oHtml.style.top = + oHtml.style.left = ""; + + div.parentNode.removeChild(div); + } + + apf.removeListener(document, "mouseup", mUp); + apf.removeListener(document, "mousemove", mMove); + }); + } + + } + + this.$btnUp = function(oHtml){ + this.parentNode.$setStyleClass(oHtml, "", ["down"], true); + + if (this.disabled) + return; + + if (this.parentNode.$order && this.$btnPressed) { + this.$dragging = false; + + $btnSet.call(this, oHtml); + } + + this.$btnPressed = false; + + this.parentNode.dispatchEvent("tabselectmouseup"); + } + + this.$btnOut = function(oHtml){ + this.parentNode.$setStyleClass(oHtml, "", ["over"], true); + + this.canHaveChildren = true; + this.$dragging = false; + this.$btnPressed = false; + } + + /**** Init ****/ + + this.$canLeechSkin = true; + + this.addEventListener("prop.class", function(e){ + apf.setStyleClass(this.$button, e.value, this.$lastClassValueBtn ? [this.$lastClassValueBtn] : null); + this.$lastClassValueBtn = e.value; + }); + + this.$draw = function(isSkinSwitch){ + this.skinName = this.parentNode.skinName; + + var sType = this.getAttribute("type") + if (sType) { + this.fake = true; + this.relPage = this.parentNode.getPage(sType) || null; + } + + if (this.parentNode.$hasButtons) { + //this.parentNode.$removeEditable(); //@todo multilingual support is broken when using dom + + this.parentNode.$getNewContext("button"); + var elBtn = this.parentNode.$getLayoutNode("button"); + elBtn.setAttribute(this.parentNode.$getOption("main", "select") || "onmousedown", + 'apf.lookup(' + this.$uniqueId + ').$btnDown(this, event);'); + elBtn.setAttribute("onmouseup", + 'apf.lookup(' + this.$uniqueId + ').$btnUp(this)'); + elBtn.setAttribute("onmouseover", 'var o = apf.lookup(' + + this.$uniqueId + ').parentNode;if(apf.lookup(' + this.$uniqueId + + ') != o.$activepage' + (this.parentNode.overactivetab ? " || true" : "") + ') o.$setStyleClass(this, "over", null, true);'); + elBtn.setAttribute("onmouseout", 'var o = apf.lookup(' + + this.$uniqueId + ');o&&o.$btnOut(this, event);'); + + //var cssClass = this.getAttribute("class"); + //if (cssClass) { + // apf.setStyleClass(elBtn, cssClass); + // this.$lastClassValueBtn = cssClass; + //} + + this.$button = apf.insertHtmlNode(elBtn, this.parentNode.$buttons); + + var closebtn = this.closebtn = this.getAttribute("closebtn"); + if ((apf.isTrue(closebtn) || ((this.parentNode.buttons || "").indexOf("close") > -1 && !apf.isFalse(closebtn)))) + this.$propHandlers["closebtn"].call(this, true); + + + if (this.parentNode.$scale) { + var w = apf.getHtmlInnerWidth(this.parentNode.$buttons); + var l = this.parentNode.getPages().length; + this.$button.style.width = Math.round(Math.min(w/l, this.parentNode.$maxBtnWidth)) + "px"; + } + + + if (!isSkinSwitch && this.nextSibling && this.nextSibling.$button) + this.$button.parentNode.insertBefore(this.$button, this.nextSibling.$button); + + this.$button.host = this; + } + + if (this.fake) + return; + + if (this.$ext) + this.$ext.parentNode.removeChild(this.$ext); //@todo mem leaks? + + this.$ext = this.parentNode.$getExternal("page", + this.parentNode.oPages, null, this); + this.$ext.host = this; + + this.$int = this.parentNode + .$getLayoutNode("page", "container", this.$ext); + //if (this.$int) + //this.$int.setAttribute("id", this.$int.getAttribute("id")); + + //@todo this doesnt support hidden nodes. + if (this.$isLast) + this.$last(); + if (this.$isFirst) + this.$first(); + }; + + this.$destroy = function(){ + if (this.$button) { + if (this.parentNode && !this.parentNode.$amlDestroyed + && this.$button.parentNode) + this.$button.parentNode.removeChild(this.$button); + + this.$button.host = null; + this.$button = null; + } + }; + + + this.$getActiveElements = function() { + // init $activeElements + if (!this.$activeElements) { + this.$activeElements = { + $button : this.$button + } + } + + return this.$activeElements; + } + +}).call(apf.page.prototype = new apf.Presentation()); + +apf.aml.setElement("page", apf.page); + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/elements/pager.js)SIZE(9037)TIME(Fri, 14 Oct 2011 08:01:44 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + + +/** + * This elements displays buttons which can be used to navigate between some + * parts of data, for example between parts of article + * + * @define pager + * @attribute {String} range determines how much page buttons is displayed + * @attribute {String} previous determines the caption of "go to previous page" button + * @attribute {String} next determines the caption of "go to next page" button + * + * @inherits apf.Presentation + * @inherits apf.StandardBinding + * @inherits apf.DataAction + * + * @author + * @version %I%, %G% + * + * @define bindings + * @allowchild current, total + * + * @binding current Determines which page is currently selected + * @binding total Determines the number of pages. + * + */ +apf.pager = function(struct, tagName){ + this.$init(tagName || "pager", apf.NODE_VISIBLE, struct); +}; + +(function() { + this.previous = "Previous"; + this.next = "Next"; + this.range = 5; + this.curpage = 1; + this.totalpages = 0; + this.autohide = false; + this.oEmpty = null; + + //1 = force no bind rule, 2 = force bind rule + this.$attrExcludePropBind = apf.extend({ + pageload : 1 + }, this.$attrExcludePropBind); + + this.$supportedProperties.push("range", "curpage", "totalpages", + "previous", "next", "autohide", "pageload"); + + this.$booleanProperties["autohide"] = true; + + this.$propHandlers["curpage"] = function(value){ + this.gotoPage(value); + }; + + /** + * Selects page depends on its number or jump length + * + * @param {Number} pageNr number of choosen page + * @param {Number} pageDelta length of jump which should be done between + * current page and new selected page + */ + this.gotoPage = function(pageNr, pageDelta, userAction) { + if (userAction && this.disabled) + return; + + var lastCurpage = this.curpage; + this.curpage = pageNr || this.curpage + pageDelta; + if (lastCurpage != this.curpage) + this.setProperty("curpage", this.curpage); + + //Sanity checks + if (this.curpage < 1) + this.curpage = 1; + else if (this.totalpages && this.curpage > this.totalpages) + this.curpage = this.totalpages; + + if (this.dispatchEvent("beforepagechange", {page:this.curpage}) === false) + return false; + + var model = this.getModel(true), + _self = this; + if (model) { + model.$loadFrom(this.pageload, { + xmlNode : this.xmlRoot, + page : this.curpage, + callback : function(){ + _self.dispatchEvent("afterpagechange", {page:_self.curpage}); + } + }); + } + else { + //@todo is this the best way to detect a model? + $setTimeout(function(){ + var model = _self.getModel(true); + if (model) { + model.$loadFrom(_self.pageload, { + xmlNode : _self.xmlRoot, + page : _self.curpage, + callback : function(){ + _self.dispatchEvent("afterpagechange", {page:_self.curpage}); + } + }); + } + + _self.removeEventListener("afterload", arguments.callee); + }); + } + }; + + this.addEventListener("$clear", function(e){ + return false; + }); + + this.$setClearMessage = function(msg, type){ + if (!this.$empty) { + this.$empty = this.$container.ownerDocument.createElement("span"); + this.$setStyleClass(this.$empty, "loader"); + } + + if (type == "loading") { + this.$setStyleClass(this.$ext, this.$baseCSSname + "Loading"); + this.$container.appendChild(this.$empty); + } + } + + this.$removeClearMessage = function(){ + if (this.$empty && this.$empty.parentNode) { + this.$empty.parentNode.removeChild(this.$empty); + this.$setStyleClass(this.$ext, "", [this.$baseCSSname + "Loading"]); + } + } + + this.$draw = function() { + this.$ext = this.$getExternal("main"); + this.$container = this.$getLayoutNode("main", "container", this.$ext); + }; + + this.$load = function(xmlRoot) { + this.setProperty("curpage", parseInt(this.$applyBindRule("current", xmlRoot))); + this.setProperty("totalpages", parseInt(this.$applyBindRule("total", xmlRoot))); + + var curpage = this.curpage, + totalpages = this.totalpages, + nodes = [], + btn; + + this.$container.innerHTML = ""; + + if (!totalpages) + return; + + if (curpage != 1 || !this.autohide) { + this.$getNewContext("button"); + btn = this.$getLayoutNode("button"); + this.$getLayoutNode("button", "caption").nodeValue = this.previous; + this.$setStyleClass(btn, "previous"); + + if (curpage != 1) { + btn.setAttribute("onclick", "apf.lookup(" + this.$uniqueId + + ").gotoPage(null, -1, true)"); + btn.setAttribute("onmousedown", 'apf.setStyleClass(this, "down");'); + btn.setAttribute("onmouseup", 'apf.setStyleClass(this,"", ["down"]);'); + } + else { + this.$setStyleClass(btn, "disabled"); + } + + nodes.push(btn); + } + + var rlow = Math.floor(this.range / 2), + // rhigh = Math.ceil(this.range / 2); + start = Math.max(1, curpage - rlow), + end = Math.min(totalpages + 1, start + this.range), + i; + if (end - start < this.range && start != 1) + start = Math.max(end - this.range, 1); + + for (i = start; i < end; i++) { + this.$getNewContext("button"); + btn = this.$getLayoutNode("button"); + this.$getLayoutNode("button", "caption").nodeValue = i; + btn.setAttribute("onclick", "apf.lookup(" + this.$uniqueId + + ").gotoPage(" + i + ", null, true)"); + btn.setAttribute("onmousedown", 'apf.setStyleClass(this, "down");'); + btn.setAttribute("onmouseup", 'apf.setStyleClass(this,"", ["down"]);'); + nodes.push(btn); + + if (i == curpage) + this.$setStyleClass(btn, "current"); + } + + if (curpage != totalpages || !this.autohide) { + this.$getNewContext("button"); + btn = this.$getLayoutNode("button"); + this.$getLayoutNode("button", "caption").nodeValue = this.next; + this.$setStyleClass(btn, "next"); + + if (curpage != totalpages) { + btn.setAttribute("onclick", "apf.lookup(" + this.$uniqueId + + ").gotoPage(null, 1, true)"); + btn.setAttribute("onmousedown", 'apf.setStyleClass(this, "down");'); + btn.setAttribute("onmouseup", 'apf.setStyleClass(this,"", ["down"]);'); + } + else { + this.$setStyleClass(btn, "disabled"); + } + + nodes.push(btn); + } + + apf.insertHtmlNodes(nodes, this.$container); + + if (this.$empty) + this.$container.appendChild(this.$empty); + } + + +}).call(apf.pager.prototype = new apf.StandardBinding()); + + +apf.aml.setElement("pager", apf.pager); +apf.aml.setElement("total", apf.BindingRule); +apf.aml.setElement("current", apf.BindingRule); + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/elements/palette.js)SIZE(5945)TIME(Fri, 14 Oct 2011 08:01:44 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/elements/param.js)SIZE(1681)TIME(Fri, 14 Oct 2011 08:01:44 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + + +/** + * element specifying an argument of a method in an rpc element. + * @attribute {String} name the argument name. + * @attribute {String} [value] the value of the argument. + * @attribute {String} [default] the default value of the argument. If + * no value is specified when this function + * is called, the default value is used. + */ +apf.param = function(struct, tagName){ + this.$init(tagName || "param", apf.NODE_HIDDEN, struct); +}; + +apf.param.prototype = new apf.AmlElement(); +apf.param.prototype.$parsePrio = "002"; +apf.aml.setElement("variable", apf.param); //backwards compatibility +apf.aml.setElement("param", apf.param); + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/elements/persist.js)SIZE(17598)TIME(Fri, 14 Oct 2011 08:01:44 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/elements/portal.js)SIZE(25076)TIME(Fri, 14 Oct 2011 08:01:44 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/elements/progressbar.js)SIZE(8709)TIME(Fri, 14 Oct 2011 08:01:44 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + + +/** + * Element graphically representing a percentage value which increases + * automatically with time. This element is most often used to show the progress + * of a process. The progress can be either indicative or exact. + * Example: + * This example shows a progress bar that is only visible when an application is + * synchronizing it's offline changes. When in this process it shows the exact + * progress of the sync process. + * + * + * + * + * @constructor + * @allowchild {smartbinding} + * @addnode elements:progressbar + * + * @inherits apf.StandardBinding + * @inherits apf.DataAction + * + * @author Ruben Daniels (ruben AT ajax DOT org) + * @version %I%, %G% + * @since 0.9 + * + * @binding value Determines the way the value for the element is retrieved + * from the bound data. + * Example: + * Sets the progress position based on data loaded into this component. + * + * + * + * + * + * + * Example: + * A shorter way to write this is: + * + * + * + * + * + * + */ +apf.progress = function(struct, tagName){ + this.$init(tagName || "progress", apf.NODE_VISIBLE, struct); +}; +apf.progressbar = function(struct, tagName){ + this.$init(tagName || "progressbar", apf.NODE_VISIBLE, struct); +}; + +(function(){ + + this.implement(apf.DataAction); + + + this.$focussable = false; // This object can get the focus + + /**** Properties and Attributes ****/ + + this.value = 0; + this.min = 0; + this.max = 100; + + this.$running = false; + this.$timer; + + /** + * @attribute {Boolean} autostart whether the progressbar starts automatically. + * @attribute {Boolean} autohide whether the progressbar hides when the progress is at 100%. Setting this to true will hide the progressbar at start when autostart is not set to true. + */ + this.$booleanProperties["autostart"] = true; + this.$booleanProperties["autohide"] = true; + + this.$supportedProperties.push("value", "min", "max", "autostart", "autohide"); + + /** + * @attribute {String} value the position of the progressbar stated between + * the min and max value. + */ + this.$propHandlers["value"] = function(value){ + this.value = parseInt(value) || this.min; + + if (this.value >= this.max) + apf.setStyleClass(this.$ext, this.$baseCSSname + "Complete", [this.$baseCSSname + "Running", this.$baseCSSname + "Half"]); + else + apf.setStyleClass(this.$ext, this.$baseCSSname + "Running", [this.$baseCSSname + "Complete"]); + + if (this.value >= this.max / 2) + apf.setStyleClass(this.$ext, this.$baseCSSname + "Half", []); + + this.oSlider.style.width = (this.value * 100 / (this.max - this.min)) + "%" + + /*Math.max(0, + Math.round((this.$ext.offsetWidth - 5) + * (this.value / (this.max - this.min)))) + "px";*/ + + this.oCaption.nodeValue = + Math.round((this.value / (this.max - this.min)) * 100) + "%"; + }; + + /** + * @attribute {Number} min the minimum value the progressbar may have. This is + * the value that the progressbar has when it's at its start position. + */ + this.$propHandlers["min"] = function(value){ + this.min = parseFloat(value); + } + + /** + * @attribute {Number} max the maximum value the progressbar may have. This is + * the value that the progressbar has when it's at its end position. + */ + this.$propHandlers["max"] = function(value){ + this.max = parseFloat(value); + } + + /**** Public Methods ****/ + + + + /** + * Sets the value of this element. This should be one of the values + * specified in the values attribute. + * @param {String} value the new value of this element + */ + this.setValue = function(value){ + this.setProperty("value", value, false, true); + }; + + /** + * Returns the current value of this element. + * @return {String} + */ + this.getValue = function(){ + return this.value; + }; + + + + /** + * Resets the progress indicator. + * @param {Boolean} restart whether a this.$timer should start with a new indicative progress indicator. + */ + this.clear = function(){ + this.$clear(); + } + + this.$clear = function(restart, restart_time){ + clearInterval(this.$timer); + this.setValue(this.min); + //this.oSlider.style.display = "none"; + apf.setStyleClass(this.$ext, "", [this.$baseCSSname + "Running", this.$baseCSSname + "Complete"]); + + if (restart) { + var _self = this; + this.$timer = setInterval(function(){ + _self.start(restart_time); + }); + } + + if (this.autohide) + this.hide(); + + this.$running = false; + }; + + /** + * Starts the progress indicator. + * @param {Number} start the time between each step in milliseconds. + */ + this.start = function(time){ + if (this.autohide) + this.show(); + + clearInterval(this.$timer); + + //if (this.value == this.max) + //this.setValue(this.min + (this.max - this.min) * 0.5); + + //this.oSlider.style.display = "block"; + var _self = this; + this.$timer = setInterval(function(){ + if (_self.$amlDestroyed) + clearInterval(_self.$timer); + else + _self.$step(); + }, time || 1000); + this.$setStyleClass(this.$ext, this.$baseCSSname + "Running"); + }; + + /** + * Pauses the progress indicator. + */ + this.pause = function(){ + clearInterval(this.$timer); + }; + + /** + * Stops the progress indicator from moving. + * @param {Boolean} restart whether a this.$timer should start with a new indicative progress indicator. + */ + this.stop = function(restart, time, restart_time){ + clearInterval(this.$timer); + this.setValue(this.max); + + var _self = this; + this.$timer = setInterval(function(){ + _self.$clear(restart, (restart_time || 0)); + }, time || 500); + }; + + /**** Private methods ****/ + + this.$step = function(){ + if (this.value == this.max) + return; + + this.setValue(this.value + 1); + }; + + /**** Init ****/ + + this.$draw = function(clear, parentNode, Node, transform){ + //Build Main Skin + this.$ext = this.$getExternal(); + this.oSlider = this.$getLayoutNode("main", "progress", this.$ext); + this.oCaption = this.$getLayoutNode("main", "caption", this.$ext); + }; + + this.$loadAml = function(x){ + if (this.autostart) + this.start(); + + if (this.autohide) + this.hide(); + }; + +}).call(apf.progressbar.prototype = new apf.StandardBinding()); + + +apf.progress.prototype = apf.progressbar.prototype; + +apf.aml.setElement("progress", apf.progress); +apf.aml.setElement("progressbar", apf.progressbar); + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/elements/propedit.js)SIZE(46649)TIME(Fri, 14 Oct 2011 08:01:44 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + + +//@todo There is a lot of dead code in here (also in the skin) remove it + +/** + * Element providing a two column grid with properties and values. The values + * are editable using apf elements. + * + * @constructor + * @define propedit + * @addnode elements + * + * @author Ruben Daniels (ruben AT ajax DOT org) + * @version %I%, %G% + * @since 0.4 + * + * @inherits apf.DataBinding + */ +apf.propedit = function(struct, tagName){ + this.$init(tagName || "propedit", apf.NODE_VISIBLE, struct); + + //this.$headings = [], + //this.$cssRules = []; //@todo Needs to be reset; + this.$nodes = []; + //this.$lastOpened = {}; + + this.$editors = {}; + + + this.$dynCssClasses = []; + +}; + +(function(){ + this.$init(function(){ + this.addEventListener("keydown", keyHandler, true); + }); + + + this.implement(apf.Cache); + + + this.implement(apf.DataAction); + + + this.$focussable = true; // This object can get the focus + this.$isTreeArch = true; // This element has a tree architecture + this.$isWindowContainer = -1; + + this.startClosed = true; + this.$animType = apf.tween.NORMAL; + this.$animSteps = 3; + this.$animSpeed = 20; + + this.$useiframe = 0; + + //1 = force no bind rule, 2 = force bind rule + this.$attrExcludePropBind = apf.extend({ + properties : 3 //only when it has an xpath + }, this.$attrExcludePropBind); + + /** + * @attribute {Boolean} iframe whether this element is rendered inside an iframe. This is only supported for IE. Default is false for datagrid and true for spreadsheet and propedit. + */ + this.$booleanProperties["iframe"] = true; + + /** + * @attribute {String} properties the {@link terms.datainstruction data instruction} + * to fetch a template definition of the layout for this component. A template + * consists of descriptions of columns (or rows for propedit) for which + * several settings are determined such as validation rules, edit component + * and selection rules. + * Example: + * This example contains a template that describes the fields in a property + * editor for xml data representing a news article. + * + * + * + * + * + * + * Yes + * No + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + */ + this.$propHandlers["properties"] = function(value){ + if (!value) + return this.clear(); + + var _self = this; + var propLoadObj = { //Should probably exist only once if expanded with xmlUpdate + load : function(data){ + _self.$loadingProps = false; + + if (typeof data == "string") + data = apf.getXml(data); + + _self.$properties = data; + + _self.$allowLoad = true; + if (_self.$loadqueue) + _self.$checkLoadQueue(); + else if (_self.xmlRoot) + _self.load(_self.xmlRoot); + delete _self.$allowLoad; + }, + + clear : function(){ + _self.$loadingProps = false; + }, + + xmlRoot : this.xmlRoot + }; + + var xml; + this.$loadingProps = true; + if (typeof value == "string") { + if (value.substr(0, 1) == "<") + propLoadObj.load(value); + else + apf.setModel(value, propLoadObj); + } + else if (value.$isModel){ + //Value is model aml element + value.register(propLoadObj); + } + else { + if (this.$properties == value && !this.caching) + return; + + //Assuming value is xml node + + propLoadObj.load(value); + + } + + if (this.$properties) + this.$prevProperties = this.$properties; + + delete this.$properties; + }; + this.addEventListener("prop.properties", function(e){ + if (!e.changed && this.$loadqueue) + this.$propHandlers["properties"].call(this, e.value); + }); + + this.$columns = ["50%", "50%"]; + this.$propHandlers["columns"] = function(value){ + this.$columns = value && value.splitSafe(",") || ["50%", "50%"]; + + if (this.$headings) { + this.$headings[0].setProperty("width", this.$columns[0]); + this.$headings[1].setProperty("width", this.$columns[1]); + } + } + + this.$propHandlers["filter"] = function(value){ + if (!value) { + this.$allowLoad = 2; + var xmlRoot = this.xmlRoot; + this.clear(); + if (this.$searchProperties) { + this.$properties = this.$searchProperties; + delete this.$searchProperties; + } + this.load(xmlRoot); + delete this.$allowLoad; + return; + } + + var p = (this.$searchProperties || (this.$searchProperties = this.$getProperties())).cloneNode(true); + var nodes = p.selectNodes("//node()[not(local-name()='group' or contains(translate(@caption, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz'), '" + value + "'))]"); + for (var i = nodes.length - 1; i >= 0; i--) + nodes[i].parentNode.removeChild(nodes[i]); + + nodes = p.selectNodes("//node()[local-name()='group' and not(node())]"); + + var parent; + for (var i = nodes.length - 1; i >= 0; i--) { + parent = nodes[i].parentNode; + parent.removeChild(nodes[i]); + if (!parent.childNodes.length && parent.parentNode) + parent.parentNode.removeChild(parent); + } + + //var t = this.$properties; + var xmlRoot = this.xmlRoot; + this.clear(); + this.$properties = p; + this.$allowLoad = 2; + this.load(xmlRoot, {force: true}); + delete this.$allowLoad; + //this.$properties = this.$searchProperties; + //this.$properties = t; + }; + + function scrollIntoView(){ + var Q = (this.current || this.$selected), + o = this.$body; + o.scrollTop = (Q.offsetTop) - 21; + } + + /**** Keyboard Support ****/ + + this.$findHtmlNode = function(id) { + return this.$pHtmlDoc.getElementById(id); + } + + + function keyHandler(e){ + var key = e.keyCode, + ctrlKey = e.ctrlKey, + shiftKey = e.shiftKey; + + var selXml = this.$lastEditor && this.$lastEditor[2], + oInt = this.$useiframe ? this.oDoc.documentElement : this.$body, + margin, node, hasScroll, hasScrollX, hasScrollY, items, lines; + + switch (key) { + case 36: + //HOME + return false; + case 35: + //END + return false; + case 107: + //+ + break; + case 37: + //LEFT + this.$slideToggle(this.$selected.firstChild); + return false; + case 107: + case 39: + //RIGHT + this.$slideToggle(this.$selected.firstChild); + + return false; + case 38: + //UP + var node = selXml; + var sNode = selXml.previousSibling; + while(sNode && sNode.nodeType != 1) sNode = sNode.previousSibling; + + if (sNode) { + var last = sNode, nodes; + while ((nodes = last.selectNodes("prop")).length) + last = nodes[nodes.length - 1]; + sNode = last; + } + else { + sNode = node.parentNode + if (sNode[apf.TAGNAME] != "prop") { + sNode = sNode.previousSibling; + while(sNode && sNode.nodeType != 1) sNode = sNode.previousSibling; + + if (sNode && sNode[apf.TAGNAME] != "prop") { + sNode = (nodes = sNode.selectNodes("prop"))[nodes.length - 1]; + while(sNode && sNode.nodeType != 1) sNode = sNode.previousSibling; + } + } + } + + if (!sNode) + return; + + var selHtml = apf.xmldb.findHtmlNode(sNode, this); + while (!selHtml.offsetWidth) + selHtml = apf.xmldb.findHtmlNode(sNode = sNode.parentNode, this); + + var top = apf.getAbsolutePosition(selHtml, this.$body)[1] + - (selHtml.offsetHeight/2); + if (top <= this.$ext.scrollTop) + this.$ext.scrollTop = top; + + this.select(selHtml); + + return false; + case 40: + //DOWN + var node, sNode = (node = selXml).selectSingleNode("prop") || node.nextSibling; + do { + while(sNode && (sNode.nodeType != 1 || sNode[apf.TAGNAME] != "prop")) + sNode = sNode.nextSibling; + + if (!sNode) { + sNode = node.parentNode.nextSibling; + if (sNode && sNode[apf.TAGNAME] != "prop") + sNode = sNode.selectSingleNode("prop"); + } + }while(sNode && sNode.nodeType != 1); + + if (!sNode) + return; + + var selHtml = apf.xmldb.findHtmlNode(sNode, this); + while (!selHtml.offsetWidth) + selHtml = apf.xmldb.findHtmlNode(sNode = sNode.parentNode, this); + + if (sNode == node) { + sNode = node.nextSibling + while(sNode && (sNode.nodeType != 1 || sNode[apf.TAGNAME] != "prop")) + sNode = sNode.nextSibling; + var selHtml = apf.xmldb.findHtmlNode(sNode, this); + } + + var top = apf.getAbsolutePosition(selHtml, this.$body)[1] + + (selHtml.offsetHeight/2); + if (top > this.$ext.scrollTop + this.$ext.offsetHeight) + this.$ext.scrollTop = top - this.$ext.offsetHeight; + + this.select(selHtml); + + return false; + }; + } + + + + /**** Focus ****/ + // Too slow for IE + + this.$focus = function(){ + if (!this.$ext || (apf.isIE && this.$useiframe && this.cssfix)) //@todo fix this by fixing focussing for this component + return; + + this.$setStyleClass(this.oFocus || this.$ext, this.$baseCSSname + "Focus"); + + if (this.oDoc) + this.$setStyleClass(this.oDoc.documentElement, this.$baseCSSname + "Focus"); + + if (this.$lastEditor) + this.$lastEditor[0].$focus(); + }; + + this.$blur = function(){ + + if (this.renaming) + this.stopRename(null, true); + + + //@todo fix this by fixing focussing for this component + if (!this.$ext || (apf.isIE && this.$useiframe && this.cssfix)) + return; + + this.$setStyleClass(this.oFocus || this.$ext, "", [this.$baseCSSname + "Focus"]); + + if (this.oDoc) + this.$setStyleClass(this.oDoc.documentElement, "", [this.$baseCSSname + "Focus"]); + + if (this.$lastEditor) + this.$lastEditor[0].$blur(); + }; + + /**** Sliding functions ****/ + + this.$slideToggle = function(htmlNode){ + container = htmlNode.parentNode.lastChild; + + if (apf.getStyle(container, "display") == "block") { + htmlNode.className = htmlNode.className.replace(/min/, "plus"); + this.$slideClose(container); + } + else { + htmlNode.className = htmlNode.className.replace(/plus/, "min"); + this.$slideOpen(container); + } + }; + + this.$slideOpen = function(container){ + container.style.display = ""; + + apf.tween.single(container, { + type : 'scrollheight', + from : 3, + diff : -2, + to : container.scrollHeight, + anim : this.$animType, + steps : this.$animSteps, + interval: this.$animSpeed, + onfinish: function(container){ + container.style.overflow = "visible"; + container.style.height = "auto"; + } + }); + }; + + this.$slideClose = function(container){ + container.style.height = container.offsetHeight; + container.style.overflow = "hidden"; + + apf.tween.single(container, { + type : 'scrollheight', + from : container.scrollHeight, + diff : -2, + to : 0, + anim : this.$animType, + steps : this.$animSteps, + interval: this.$animSpeed, + onfinish: function(container, data){ + container.style.display = "none"; + } + }); + }; + + this.$findContainer = function(htmlNode) { + var node = htmlNode.nextSibling; + if (!node) + return htmlNode; + return node.nodeType == 1 ? node : node.nextSibling; + }; + + /**** Databinding ****/ + + this.addEventListener("bindingsload", this.$loaddatabinding = function(e){ + var rules = e.bindings["properties"]; + if (!rules || !rules.length) + return; + + for (var i = 0, l = rules.length; i < l; i++) { + + } + }); + + this.$unloaddatabinding = function(){ + }; + + /** + * Returns a column definition object based on the column number. + * @param {Number} hid the heading number; this number is based on the sequence of the column elements. + */ + this.getColumn = function(nr){ + return this.$headings[nr || this.$lastcol || 0]; + }; + + /**** Column management ****/ + + /** + * Resizes a column. + * @param {Number} hid the heading number; this number is based on the sequence of the column elements. + * @param {Number} newsize the new size of the column. + * @todo optimize but bringing down the string concats + */ + this.resizeColumn = function(nr, newsize){ + var h = this.$headings[nr]; + h.resize(newsize); + }; + + /** + * Hides a column. + * @param {Number} hid the heading number; this number is based on the sequence of the column elements. + */ + this.hideColumn = function(nr){ + var h = this.$headings[nr]; + h.hide(); + }; + + /** + * Shows a hidden column. + * @param {Number} hid the heading number; this number is based on the sequence of the column elements. + */ + this.showColumn = function(nr){ + var h = this.$headings[nr]; + h.show(); + }; + + /**** Databinding ****/ + + /* + Property: + - caption + - editor (name of widget, lm function returning amlNode or lm template ref) + - children being aml nodes + - value (lm, only when widget is created by grid) + + validation attr: (only when widget is created by grid) + - required + - datatype + - required + - pattern + - min + - max + - maxlength + - minlength + - notnull + - checkequal + - validtest + + Group: + - name + - properties + + Move from dg to widgets: + - autocomplete with template + - dropdown with bound multicheck + + Furthermore it supports: + - properties binding rule to switch properties + - special node introspection mode + - .listAttributes() + - returns array of objects + - name + - editor + - validation rules + - .setAttribute(name, value) + - .getAttribute(name) + - .addEventListener("prop." + name); + - .removeEventListener("prop." + name); + */ + + this.$getProperties = function(xmlNode){ + if (this.properties) { + return this.$properties || false; + } + else if (this.$bindings.properties) { + var props = this.$bindings.properties; + for (var i = 0; i < props.length; i++) { + if (!props[i].match) //compile via lm + return xx; //async request entry + } + } + + return false; + } + + /** + * Forces propedit to wait for the load of a new property definition + */ + this.$canLoadData = function(){ + if (this.$allowLoad || !this.$attrBindings["properties"]) + return this.$getProperties() ? true : false; + + /* + @todo this is turned off because it competes with the signal from + prop.properties. When the properties property contains an async + statement, the prop isnt set until the call returns. This means this + component doesnt know it has the wrong property set until later. + Thats causing problems. Uncommenting this causes a problem when a + dynamic property is not refreshed whenever the model is set. + */ + else if (false) { + var _self = this; + apf.queue.add("load" + this.$uniqueId, function(){ + _self.$allowLoad = true; + _self.$checkLoadQueue(); + delete _self.$allowLoad; + }); + return false; + } + } + + this.$generateCacheId = function(xmlNode){ + var node = this.$getProperties(); + return node ? node.getAttribute(apf.xmldb.xmlIdTag) + || apf.xmldb.nodeConnect(apf.xmldb.getXmlDocId(node), node) + : 0; + } + + this.$getCurrentFragment = function(){ + var fragment = this.$container.ownerDocument.createDocumentFragment(); + fragment.properties = this.$searchProperties || this.$prevProperties || this.$properties; + + while (this.$container.childNodes.length) { + fragment.appendChild(this.$container.childNodes[0]); + } + + this.clearSelection(); + + return fragment; + }; + + this.$setCurrentFragment = function(fragment){ + this.$container.appendChild(fragment); + + if (!apf.window.hasFocus(this)) + this.blur(); + + apf.xmldb.addNodeListener(this.xmlRoot, this); //set node listener if not set yet + + //@todo most unoptimized way possible: + if (this.filter && this.$allowLoad != 2) { + delete this.$searchProperties; + this.$prevProperties = fragment.properties; + this.$propHandlers["filter"].call(this, this.filter); + } + else { + this.$xmlUpdate(null, this.xmlRoot); + this.$properties = fragment.properties; + this.select(apf.xmldb.findHtmlNode(this.$properties.selectSingleNode(".//prop"), this)); + } + }; + + this.$load = function(xmlNode){ + var p = this.$getProperties(); + if (!p) return false; + + var output = []; + var docId = this.documentId = apf.xmldb.getXmlDocId(p); + + //Add listener to XMLRoot Node + apf.xmldb.addNodeListener(xmlNode, this); //@todo apf3 potential cleanup problem + //apf.xmldb.addNodeListener(this.xmlRoot, this); + + var _self = this, doc = p.ownerDocument; + (function walk(nodes, parent, depth){ + for (var u, s, cell, sLid, pnode, html, node, i = 0, l = nodes.length; i < l; i++) { + node = nodes[i]; + _self.$getNewContext("row") + html = _self.$getLayoutNode("row"); + + if (node[apf.TAGNAME] == "group") { + _self.$getNewContext("cell"); + apf.setStyleClass(html, "heading"); + + cell = html.appendChild(_self.$getLayoutNode("cell")); + apf.setNodeValue(_self.$getLayoutNode("cell", "caption", cell), + (node.getAttribute("caption") || "").trim() || ""); //@todo for IE but seems not a good idea + + //group| + pnode = html.appendChild(doc.createElement("blockquote")); + walk(node.selectNodes("prop"), pnode, depth); + html.insertBefore(u = doc.createElement("u"), html.firstChild).appendChild(doc.createTextNode(" ")); + u.setAttribute("class", "min"); + } + else { + apf.xmldb.nodeConnect(docId, node, html, _self); + + //Build the Cells + _self.$getNewContext("cell"); + h = _self.$headings[0]; + + cell = html.appendChild(_self.$setStyleClass(_self.$getLayoutNode("cell"), h.$className)); + apf.setNodeValue(_self.$getLayoutNode("cell", "caption", cell), + (node.getAttribute("caption") || "").trim() || ""); //@todo for IE but seems not a good idea + + if (depth) + cell.firstChild.setAttribute("style", "padding-left:" + (depth * 15) + "px"); + + _self.$getNewContext("cell"); + h = _self.$headings[1]; + + cell = html.appendChild(_self.$setStyleClass(_self.$getLayoutNode("cell"), h.$className)); + apf.setNodeValue(_self.$getLayoutNode("cell", "caption", cell), + ((apf.lm.compile(node.getAttribute("value"), {nostring: true}))(_self.xmlRoot) || "") || ""); //@todo for IE but seems not a good idea + + if ((s = node.selectNodes("prop")).length) { + pnode = html.appendChild(doc.createElement("blockquote")); + pnode.setAttribute("style", "display:none;overflow:hidden;height:0;"); + walk(s, _self.$getLayoutNode("heading", "container", pnode), depth + 1); + + //Add opener + html.insertBefore(u = doc.createElement("u"), html.firstChild).appendChild(doc.createTextNode(" ")); + u.setAttribute("class", "plus"); + } + } + + if (!parent) + output.push(html); + else + parent.appendChild(html); + } + })(p.selectNodes("group|prop"), null, 0); + + apf.insertHtmlNodes(output, this.$body); + + this.setProperty("root", this.xmlRoot); //or xmlNode ?? + + //@todo select the first one + var prop = p.selectSingleNode(".//prop"); + if (prop) { + this.select(this.$findHtmlNode( + prop.getAttribute(apf.xmldb.xmlIdTag) + + "|" + this.$uniqueId)); + } + + //@todo most unoptimized way possible: + if (this.filter && this.$allowLoad != 2) { + delete this.$searchProperties; + this.$propHandlers["filter"].call(this, this.filter); + } + } + + this.$xmlUpdate = function(action, xmlNode, listenNode, UndoObj){ + if (xmlNode != this.xmlRoot) + return; + + if (UndoObj && this.$lastEditor[0] == UndoObj.amlNode) { + this.$lastEditor[1].firstChild.innerHTML = + ((apf.lm.compile(this.$lastEditor[2].getAttribute("value"), { + nostring: true + }))(this.xmlRoot) || "") || ""; + } + else { + var p = this.$getProperties(); + if (!p) + return; + + var node, htmlNode, nodes = p.selectNodes(".//group/prop"); + for (var i = 0, l = nodes.length; i < l; i++) { + node = nodes[i]; + htmlNode = this.$findHtmlNode( + node.getAttribute(apf.xmldb.xmlIdTag) + "|" + this.$uniqueId); + + htmlNode.childNodes[htmlNode.firstChild.tagName == "U" ? 2 : 1] + .firstChild.innerHTML = + ((apf.lm.compile(node.getAttribute("value"), { + nostring: true + }))(this.xmlRoot) || "") || ""; + } + } + } + + this.$hideEditor = function(remove){ + if (this.$lastEditor) { + //this.$lastEditor[0].$blur(); + this.$lastEditor[0].setProperty("visible", false); + + if (remove) { + var pNode = this.$lastEditor[0].$ext.parentNode; + pNode.removeChild(this.$lastEditor[0].$ext); + pNode.removeAttribute("id"); + delete pNode.onresize; + } + + var nodes = this.$lastEditor[1].childNodes; + for (var i = 0, l = nodes.length; i < l; i++) { + if (!nodes[i].host) + nodes[i].style.display = ""; + } + + delete this.$lastEditor; + } + } + + this.clearSelection = function(){ + this.$setStyleClass(this.$selected, "", ["selected"]); + delete this.$selected; + this.$hideEditor(); + } + + this.select = function(htmlNode){ + if (this.disabled) //@todo apf3.0 userAction + return; + + + if (this.$selected == htmlNode) { + /*var oEditor = this.$lastEditor[0]; + $setTimeout(function(){ + oEditor.focus(); + });*/ + + return; + } + + if (this.$selected) + this.$setStyleClass(this.$selected, "", ["selected"]); + + this.$setStyleClass(htmlNode, "selected"); + this.$selected = htmlNode; + + this.$hideEditor(); + + var prop = apf.xmldb.getNode(htmlNode); + var _self = this; + + this.setProperty("selected", prop); + + /* + - editor (name of widget, lm function returning amlNode or lm template ref) + - children being aml nodes + */ + var editParent = this.$selected.childNodes[this.$selected.firstChild.tagName == "U" ? 2 : 1]; + var oEditor, editor = prop.getAttribute("editor"); + var ceditor = apf.lm.compile(editor, {xpathmode: 2}); + if (ceditor.type == 2) { + + + if (!this.$editors[editor]) { + var constr = apf.namespaces[apf.ns.aml].elements[editor]; + var isTextbox = "textarea|textbox|secret".indexOf(editor) > -1; + var info = { + htmlNode : editParent, + width : "100%+2", + height : 19, + style : "position:relative;", //z-index:10000 + value : "[{" + this.id + ".root}::" + + (v = prop.getAttribute("value")).substr(1, v.length - 2) + + "]", + focussable : false, + realtime : !isTextbox + }; + if (isTextbox) { + info.focusselect = true; + info.onkeydown = function(e){ + if (e.keyCode == 13) + this.change(this.getValue()); + } + } + else if (editor == "checkbox") + info.values = "true|false"; + + //@todo copy all non-known properties of the prop element + + if (constr.prototype.hasFeature(apf.__MULTISELECT__)) { + info.caption = "[text()]"; + info.eachvalue = "[@value]"; + info.each = "item"; + info.model = "{apf.xmldb.getElementById('" + + prop.getAttribute(apf.xmldb.xmlIdTag) + "')}"; + } + + oEditor = this.$editors[editor] = new constr(info); + + var box = apf.getBox(apf.getStyle(oEditor.$ext, "margin")); + if (box[1] || box[3]) { + oEditor.setAttribute("width", "100%+2-" + (box[1] + box[3])); + } + else if (!box[3]) + oEditor.$ext.style.marginLeft = "-1px"; + + //oEditor.$focussable = false; + /*oEditor.addEventListener("focus", function(){ + _self.focus(); + this.$focus(); + });*/ + oEditor.parentNode = this; + oEditor.$focusParent = this; + oEditor.setAttribute("focussable", "true"); + //delete oEditor.parentNode; + + //@todo set actiontracker + oEditor.$parentId = editParent.getAttribute("id"); + oEditor.$parentRsz = editParent.onresize; + + //Patch oEditor to forward change + oEditor.$executeAction = function(atAction, args, action, xmlNode, noevent, contextNode, multiple){ + if (atAction == "setAttribute" && !args[2]) + atAction = "removeAttribute"; + + this.parentNode.$executeAction.call(this.parentNode, + atAction, args, action, xmlNode, noevent, contextNode, multiple); + } + } + else { + oEditor = this.$editors[editor]; + + if (oEditor.hasFeature(apf.__MULTISELECT__)) { + oEditor.setAttribute("model", "{apf.xmldb.getElementById('" + + prop.getAttribute(apf.xmldb.xmlIdTag) + "')}"); + } + + oEditor.setAttribute("value", "[{" + this.id + ".root}::" + + (v = prop.getAttribute("value")).substr(1, v.length - 2) + + "]"); + + oEditor.setProperty("visible", true); + if (oEditor.$ext.parentNode + && oEditor.$ext.parentNode.nodeType == 1 + && !apf.hasSingleResizeEvent) { + if (!oEditor.$parentRsz) + oEditor.$parentRsz = oEditor.$ext.parentNode.onresize; + oEditor.$ext.parentNode.removeAttribute("id"); + delete oEditor.$ext.parentNode.onresize; + } + + editParent.appendChild(oEditor.$ext); + editParent.setAttribute("id", editParent.$parentId); + if (oEditor.$parentRsz && !apf.hasSingleResizeEvent) { + editParent.onresize = oEditor.$parentRsz; + editParent.onresize(); + } + } + + /*setTimeout(function(){ + oEditor.focus(); + });*/ + } + else { + //Create dropdown + + var obj = ceditor.call(this, this.xmlRoot); + if (obj.localName == "template") { + //add template contents to dropped area + } + else { + //add xml into dropped area + } + } + + var nodes = editParent.childNodes; + for (var i = 0, l = nodes.length; i < l; i++) { + if (!nodes[i].host) + nodes[i].style.display = "none"; + } + + this.$lastEditor = [oEditor, editParent, prop]; + } + + this.addEventListener("$clear", function(e){ + this.$hideEditor(true); + }); + + /*this.addEventListener("blur", function(){ + if (this.$lastEditor) + this.$lastEditor[0].$blur(); + }); + + this.addEventListener("focus", function(){ + if (this.$lastEditor) + this.$lastEditor[0].$focus(); + });*/ + + /**** Init ****/ + + this.$draw = function(){ + //Build Main Skin + this.$ext = this.$getExternal(); + this.$body = this.$getLayoutNode("main", "body", this.$ext); + this.$head = this.$getLayoutNode("main", "head", this.$ext); + this.$pointer = this.$getLayoutNode("main", "pointer", this.$ext); + this.$container = this.$body; + + if (this.$head.firstChild) + this.$head.removeChild(this.$head.firstChild); + if (this.$body.firstChild) + this.$body.removeChild(this.$body.firstChild); + + var widthdiff = this.$widthdiff = this.$getOption("main", "widthdiff") || 0; + this.$defaultwidth = this.$getOption("main", "defaultwidth") || "100"; + this.$useiframe = apf.isIE && (apf.isTrue(this.$getOption("main", "iframe")) || this.iframe); + + var _self = this; + + //Initialize Iframe + if (this.$useiframe && !this.oIframe) { + //this.$body.style.overflow = "hidden"; + //var sInt = this.$body.outerHTML + var sClass = this.$body.className; + //this.$body.parentNode.removeChild(this.$body); + this.oIframe = this.$body.appendChild(document.createElement(apf.isIE + ? "" + : "iframe")); + this.oIframe.frameBorder = 0; + this.oWin = this.oIframe.contentWindow; + this.oDoc = this.oWin.document; + this.oDoc.write('\ + \ + \ + \ + \ + '); + //Import CSS + //this.oDoc.body.innerHTML = sInt; + this.$body = this.oDoc.body;//.firstChild; + this.$body.className = sClass;//this.oIframe.parentNode.className; + this.oDoc.documentElement.className = this.$ext.className; + //this.oDoc.body.className = this.$ext.className; + + apf.skins.loadCssInWindow(this.skinName, this.oWin, this.mediaPath, this.iconPath); + + if (apf.isIE) //@todo this can be removed when focussing is fixed for this component + this.$setStyleClass(this.oDoc.documentElement, this.$baseCSSname + "Focus"); + + apf.convertIframe(this.oIframe, true); + + if (apf.getStyle(this.oDoc.documentElement, "overflowY") == "auto") { + //@todo ie only + this.oIframe.onresize = function(){ + _self.$head.style.marginRight = + _self.oDoc.documentElement.scrollHeight > _self.oDoc.documentElement.offsetHeight + ? "16px" : "0"; + } + + this.addEventListener("afterload", this.oIframe.onresize); + this.addEventListener("xmlupdate", this.oIframe.onresize); + } + + this.oDoc.documentElement.onmousedown = function(e){ + if (!e) e = _self.oWin.event; + if ((e.srcElement || e.target).tagName == "HTML") + apf.popup.forceHide(); + } + + this.oDoc.documentElement.onscroll = + function(){ + if (_self.$isFixedGrid) + _self.$head.scrollLeft = _self.oDoc.documentElement.scrollLeft; + }; + } + else { + if (apf.getStyle(this.$body, "overflowY") == "auto") { + this.$resize = function(){ + _self.$head.style.marginRight = + _self.$body.scrollHeight > _self.$body.offsetHeight + ? "16px" : "0"; + } + + + this.addEventListener("resize", this.$resize); + + + this.addEventListener("afterload", this.$resize); + this.addEventListener("xmlupdate", this.$resize); + } + + this.$body.onmousedown = function(e){ + if (!e) e = event; + if ((e.srcElement || e.target) == this) + apf.popup.forceHide(); + } + + this.$body.onscroll = + function(){ + if (_self.$isFixedGrid) + _self.$head.scrollLeft = _self.$body.scrollLeft; + }; + } + + var _self = this; + this.$body.onmousedown = function(e){ + if (!e) e = event; + var target = e.srcElement || e.target; + + if (target == this) return; + + if (target.tagName == "U") { + _self.$slideToggle(target); + return; + } + + while (target.host || (target.getAttribute(apf.xmldb.htmlIdTag) || "").indexOf("|") == -1) { + target = target.parentNode; + if (target == this) return; + } + + _self.select(target); + } + }; + + this.$loadAml = function(x){ + //Create two columns + this.$headings = [ + new apf.BindingColumnRule().$draw(this, "Property", this.$columns[0], "first"), + new apf.BindingColumnRule().$draw(this, "Value", this.$columns[1]) + ]; + }; + + this.$destroy = function(){ + apf.popup.removeContent(this.$uniqueId); + + for (var prop in this.$editors) { + this.$editors[prop].destroy(); + } + + this.$ext.onclick = null; + }; + +}).call(apf.propedit.prototype = new apf.DataBinding()); + + +apf.aml.setElement("propedit", apf.propedit); +apf.aml.setElement("column", apf.BindingColumnRule); +apf.aml.setElement("description", apf.BindingRule); +apf.aml.setElement("color", apf.BindingRule); + + + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/elements/radiobutton.js)SIZE(17104)TIME(Tue, 13 Dec 2011 13:33:58 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + +/** + * Element displaying a two state button which is one of a grouped set. + * Only one of these buttons in the set can be selected at the same time. + * Example: + * + * + * Option 1 + * Option 2 + * Option 3 + * Option 4 + * + * + * Example: + * This example shows radio buttons with an explicit group set: + * + * Options + * Option 1 + * Option 2 + * + * Choices + * + * Choice 1 + * Choice 2 + * + * + * + * @constructor + * @define radiobutton + * @allowchild {smartbinding} + * @addnode elements + * + * @inherits apf.Presentation + * + * @author Ruben Daniels (ruben AT ajax DOT org) + * @version %I%, %G% + * @since 0.4 + * + * @binding value Determines the way the value for the element is retrieved + * from the bound data. + * Example: + * Sets the selection based on data loaded into this component. + * + * Choice 1 + * Choice 2 + * + * + * + * + * + * Example: + * A shorter way to write this is: + * + * Choice 1 + * Choice 2 + * + * + * @event click Fires when the user presses a mousebutton while over this element and then let's the mousebutton go. + * @see baseclass.amlnode.event.afterchange + */ +apf.radiobutton = function(struct, tagName){ + this.$init(tagName || "radiobutton", apf.NODE_VISIBLE, struct); + + /*this.$constructor = apf.radiobutton; + var fEl = apf.aml.setElement("radiobutton", function(){ + this.$init(tagName || "radiobutton", apf.NODE_VISIBLE, struct); + }); + fEl.prototype = apf.radiobutton.prototype; + apf.radiobutton = fEl;*/ +}; + +(function(){ + this.implement(apf.ChildValue); + this.$childProperty = "label"; + + this.$focussable = apf.KEYBOARD; // This object can get the focus + + //1 = force no bind rule, 2 = force bind rule + /*this.$attrExcludePropBind = apf.extend({ + selected: 1 + }, this.$attrExcludePropBind);*/ + + /**** Properties and Attributes ****/ + + this.$booleanProperties["selected"] = true; + this.$supportedProperties.push("value", "background", "group", + "label", "selected", "tooltip", "icon"); + + /** + * @attribute {String} group the name of the group to which this radio + * button belongs. Only one item in the group can be selected at the same + * time. When no group is specified the parent container functions as the + * group; only one radiobutton within that parent can be selected. + */ + this.$propHandlers["group"] = function(value){ + if (!this.$ext) + return; + + if (this.$group && this.$group.$removeRadio) + this.$group.$removeRadio(this); + + if (!value) { + this.$group = null; + return; + } + + var group = typeof value == "string" + ? + + apf.nameserver.get("group", value) + + : value; + if (!group) { + + group = apf.nameserver.register("group", value, + new apf.$group()); + group.setAttribute("id", value); + group.dispatchEvent("DOMNodeInsertedIntoDocument"); + group.parentNode = this; + + } + this.$group = group; + + if (this.oInput) + this.oInput.setAttribute("name", value); + + this.$group.$addRadio(this); + }; + + /** + * @attribute {String} tooltip the tooltip of this radio button. + */ + this.$propHandlers["tooltip"] = function(value){ + this.$ext.setAttribute("title", value); + }; + + /** + * @attribute {String} icon the icon for this radiobutton + */ + this.$propHandlers["icon"] = function(value){ + + if (!this.oIcon) return; + + + if (value) + this.$setStyleClass(this.$ext, this.$baseCSSname + "Icon"); + else + this.$setStyleClass(this.$ext, "", [this.$baseCSSname + "Icon"]); + + apf.skins.setIcon(this.oIcon, value, this.iconPath); + }; + + /** + * @attribute {String} label the label for this radiobutton + */ + this.$propHandlers["label"] = function(value){ + if (value) + this.$setStyleClass(this.$ext, "", [this.$baseCSSname + "Empty"]); + else + this.$setStyleClass(this.$ext, this.$baseCSSname + "Empty"); + + if (this.oLabel) + this.oLabel.innerHTML = value; + }; + + /** + * @attribute {String} selected whether this radiobutton is the selected one in the group it belongs to. + */ + this.$propHandlers["selected"] = function(value){ + if (!this.$group) + return; + + if (value) + this.$group.setProperty("value", this.value); + //else if (this.$group.value == this.value) + //this.$group.setProperty("value", ""); + }; + + this.addEventListener("prop.model", function(e){ + if (this.$group) + this.$group.setProperty("model", e.value); + }); + + /** + * @attribute {string} background sets a multistate background. The arguments + * are seperated by pipes '|' and are in the order of: + * 'imagefilename|mapdirection|nrofstates|imagesize' + * The mapdirection argument may have the value of 'vertical' or 'horizontal'. + * The nrofstates argument specifies the number of states the iconfile contains: + * 1 - normal + * 2 - normal, hover + * 3 - normal, hover, down + * 4 - normal, hover, down, disabled + * The imagesize argument specifies how high or wide each icon is inside the + * map, depending of the mapdirection argument. + * + * Example: + * A 3 state picture where each state is 16px high, vertically spaced + * + * background="threestates.gif|vertical|3|16" + * + * @see baseclass.basebutton + */ + this.$propHandlers["background"] = function(value){ + var oNode = this.$getLayoutNode("main", "background", this.$ext); + if (value) { + var b = value.split("|"); + this.$background = b.concat(["vertical", 2, 16].slice(b.length - 1)); + + oNode.style.backgroundImage = "url(" + this.mediaPath + b[0] + ")"; + oNode.style.backgroundRepeat = "no-repeat"; + } + else { + oNode.style.backgroundImage = ""; + oNode.style.backgroundRepeat = ""; + this.$background = null; + } + } + + /**** Public methods ****/ + + + + /** + * Sets the value of this element. This should be one of the values + * specified in the values attribute. + * @param {String} value the new value of this element + */ + this.setValue = function(value){ + this.setProperty("value", value, false, true); + }; + + /** + * Returns the current value of this element. + * @return {String} + */ + this.getValue = function(){ + return this.value; + }; + + this.select = function(){ + this.setProperty("selected", true, false, true); + } + + /*this.uncheck = function(){ + this.setProperty("selected", false, false, true); + }*/ + + this.getGroup = function(){ + return this.$group; + } + + + + /** + * Sets the selected state and related value + */ + this.$check = function(visually){ + this.$setStyleClass(this.$ext, this.$baseCSSname + "Selected"); + this.selected = true; + if (this.oInput) + this.oInput.selected = true; + this.doBgSwitch(2); + }; + + this.$uncheck = function(){ + this.$setStyleClass(this.$ext, "", [this.$baseCSSname + "Selected"]); + this.selected = false; + if (this.oInput) + this.oInput.selected = false; + this.doBgSwitch(1); + }; + + /**** Private methods ****/ + + this.$enable = function(){ + if (this.oInput) + this.oInput.disabled = false; + + var _self = this; + this.$ext.onclick = function(e){ + if (!e) e = event; + if ((e.srcElement || e.target) == this) + return; + + _self.dispatchEvent("click", { + htmlEvent: e + }); + _self.$group.change(_self.value); + } + + this.$ext.onmousedown = function(e){ + if (!e) e = event; + if ((e.srcElement || e.target) == this) + return; + + apf.setStyleClass(this, _self.$baseCSSname + "Down"); + } + + this.$ext.onmouseover = function(e){ + if (!e) e = event; + if ((e.srcElement || e.target) == this) + return; + + apf.setStyleClass(this, _self.$baseCSSname + "Over"); + } + + this.$ext.onmouseout = + this.$ext.onmouseup = function(){ + apf.setStyleClass(this, "", [_self.$baseCSSname + "Down", _self.$baseCSSname + "Over"]); + } + }; + + this.$disable = function(){ + if (this.oInput) + this.oInput.disabled = true; + + this.$ext.onclick = + this.$ext.onmousedown = + this.$ext.onmouseover = + this.$ext.onmouseout = + this.$ext.onmouseup = null; + }; + + /** + * @private + */ + this.doBgSwitch = function(nr){ + if (this.bgswitch && (this.bgoptions[1] >= nr || nr == 4)) { + if (nr == 4) + nr = this.bgoptions[1] + 1; + + var strBG = this.bgoptions[0] == "vertical" + ? "0 -" + (parseInt(this.bgoptions[2]) * (nr - 1)) + "px" + : "-" + (parseInt(this.bgoptions[2]) * (nr - 1)) + "px 0"; + + this.$getLayoutNode("main", "background", this.$ext) + .style.backgroundPosition = strBG; + } + }; + + this.$focus = function(){ + if (!this.$ext) + return; + if (this.oInput && this.oInput.disabled) + return false; + + this.$setStyleClass(this.$ext, this.$baseCSSname + "Focus"); + }; + + this.$blur = function(){ + if (!this.$ext) + return; + this.$setStyleClass(this.$ext, "", [this.$baseCSSname + "Focus"]); + }; + + /**** Keyboard support ****/ + + + this.addEventListener("keydown", function(e){ + var key = e.keyCode; + + if (key == 13 || key == 32) { + //this.check(); + //this.$group.current = this; + this.$group.change(this.value); + return false; + } + //Up + else if (key == 38) { + var node = this; + while (node && node.previousSibling) { + node = node.previousSibling; + if (node.localName == "radiobutton" && !node.disabled + && node.$group == this.$group) { + node.check(); + node.focus(); + return; + } + } + } + //Down + else if (key == 40) { + var node = this; + while (node && node.nextSibling) { + node = node.nextSibling; + if (node.localName == "radiobutton" && !node.disabled + && node.$group == this.$group) { + node.check(); + node.focus(); + return; + } + } + } + }, true); + + + /**** Init ****/ + + this.$draw = function(){ + //Build Main Skin + this.$ext = this.$getExternal(); + this.oInput = this.$getLayoutNode("main", "input", this.$ext); + this.oLabel = this.$getLayoutNode("main", "label", this.$ext); + this.oIcon = this.$getLayoutNode("main", "icon", this.$ext); + + if (this.oLabel && this.oLabel.nodeType != 1) + this.oLabel = this.oLabel.parentNode; + + //Set events + this.$enable(); + }; + + this.$childProperty = "label"; + this.$loadAml = function(x){ + if (this.group) + this.$propHandlers["group"].call(this, this.group); + + else if (this.parentNode.localName == "group") + this.$propHandlers["group"].call(this, this.parentNode); + + if (!this.$group) { + this.$propHandlers["group"].call(this, + "group" + this.parentNode.$uniqueId); + } + }; + + this.$destroy = function(){ + if (this.$group) + this.$group.$removeRadio(this); + }; + + + this.$getActiveElements = function() { + // init $activeElements + if (!this.$activeElements) { + this.$activeElements = { + oInput : this.oInput + } + } + + return this.$activeElements; + }; + +}).call(apf.radiobutton.prototype = new apf.Presentation()); + +apf.aml.setElement("radiobutton", apf.radiobutton); + +apf.$group = apf.group = function(struct, tagName){ + this.$init(tagName || "group", apf.NODE_VISIBLE, struct); + + this.implement( + apf.StandardBinding, + + apf.DataAction + + + ); + + var radiobuttons = []; + + this.$supportedProperties.push("value", "selectedItem"); + this.$propHandlers["value"] = function(value){ + for (var i = 0; i < radiobuttons.length; i++) { + if (radiobuttons[i].value == value) { + return this.setProperty("selectedItem", radiobuttons[i]); + } + } + return this.setProperty("selectedItem", null); + }; + + var lastSelected; + this.$propHandlers["selectedItem"] = function(rb){ + if (lastSelected) + lastSelected.$uncheck(); + if (!rb) + return; + + rb.$check(); + lastSelected = rb; + + for (var i = 0; i < radiobuttons.length; i++) + radiobuttons[i].setProperty("selectedItem", rb); + }; + + this.$addRadio = function(rb){ + var id = radiobuttons.push(rb) - 1; + + if (!rb.value) + rb.setProperty("value", id); + + var _self = this; + rb.addEventListener("prop.value", function(e){ + if (this.selected) + _self.setProperty("value", e.value); + else if (_self.value == e.value) + this.select(); + }); + + if (this.value && rb.value == this.value) + this.setProperty("selectedItem", rb); + else if (rb.selected) + this.setProperty("value", rb.value); + }; + + this.$removeRadio = function(rb){ + radiobuttons.remove(rb); + + if (rb.value === rb.id) + rb.setProperty("value", ""); + + if (rb.selectedItem == rb) + this.setProperty("value", null); + } + + /** + * Sets the current value of this element. + */ + this.setValue = function(value){ + this.setProperty("value", value); + }; + + /** + * Returns the current value of this element. + * @return {String} + */ + this.getValue = function(){ + return this.value; + }; + + this.$draw = function(){ + this.$ext = this.$int = this.$pHtmlNode; + } +}; +apf.$group.prototype = new apf.GuiElement(); + +apf.aml.setElement("group", apf.$group); + + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/elements/remote.js)SIZE(20984)TIME(Fri, 14 Oct 2011 08:01:44 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/elements/rpc.js)SIZE(21108)TIME(Fri, 14 Oct 2011 08:01:44 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + +/** + * Baseclass for rpc in teleport. Modules are available for + * SOAP, XML-RPC, CGI, JSON-RPC and several proprietary protocols. + * + * Example: + * Ajax.org Markup Language + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * //This function is called when the search returns + * function processSearch(data, state, extra){ + * alert(data) + * } + * + * //Execute a search for the product car + * comm.searchProduct('car', 10); + * + * + * + * Example: + * This example shows an rpc element using the xmlrpc protocol. It contains + * two methods which can be called. The return of the first method is handled + * by a javascript function called processSearch. + * + * + * + * + * + * // + * + * + * Example: + * Ajax.org Markup Language + * + * + * + * + * + * + * + * + * + * + * //This function is called when the search returns + * function processSearch(data, state, extra){ + * alert(data) + * } + * + * //Execute a search for the product car + * comm.searchProduct('car', 10); + * + * + * + * Example: + * Ajax.org Markup Language + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * //This function is called when the search returns + * function processSearch(data, state, extra){ + * alert(data) + * } + * + * //Execute a search for the product car + * comm.searchProduct('car', 10); + * + * + * + * Example: + * Ajax.org Markup Language + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * //This function is called when the search returns + * function processSearch(data, state, extra){ + * alert(data) + * } + * + * //Execute a search for the product car + * comm.searchProduct('car', 10); + * + * + * + * Example: + * Ajax.org Markup Language + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * //This function is called when the search returns + * function processSearch(data, state, extra){ + * alert(data) + * } + * + * //Execute a search for the product car + * comm.searchProduct('car', 10); + * + * + * + * @attribute {String} protocol the name of the plugin that is used + * to provide the messages. + * @attribute {Boolean} [multicall] whether the call is stacked until + * purge() is called. + * @attribute {String} [route-server] the location of the proxy script that + * allows for cross domain communication. + * @attribute {String} [http-method] the http method used to send the data. + * This attribute is only used by the cgi protocol. + * Possible values: + * post Used to store large chunks of data (on a resource). + * get Used to retrieve data from a resource. + * delete Used to delete a resource. + * head Returns only the headers. + * put Used to store data at a resource. + * @attribute {String} [method-name] the variable name used to sent the + * name of the method called to the + * server. This attribute is only used + * by the cgi protocol. + * @attribute {String} [soap-xmlns] the url that uniquely identifies the + * xml namespace for the message. This + * attribute is only used by the soap + * protocol. + * @attribute {String} [soap-prefix] the prefix that is paired with the + * message xml namespace. This attribute + * is only used by the soap protocol. + * @define rpc + * @allowchild method + * + * @constructor + * + * @author Ruben Daniels (ruben AT ajax DOT org) + * @author Mike de Boer (mike AT javeline DOT com) + * @version %I%, %G% + * @since 0.4 + * @default_private + */ +apf.rpc = function(struct, tagName){ + this.$init(tagName || "rpc", apf.NODE_HIDDEN, struct); + + if (!this.supportMulticall) + this.multicall = false; + this.stack = {}; + this.urls = {}; + this.$methods = {}; +}; + +(function(){ + this.useHTTP = true; + this.namedArguments = false; + + this["route-server"] = apf.host + "/cgi-bin/rpcproxy.cgi"; + this.autoroute = false; + + this.$auth = false; + + this.$booleanProperties["multicall"] = true; + + this.$supportedProperties.push("protocol", "type", "multicall", "http-method"); + + this.$propHandlers["route-server"] = function(value){ + this.autoroute = value ? true : false; + }; + + //@todo change this to use prototype + this.$propHandlers["protocol"] = function(value){ + if (!value) + return; + + if (!apf[value]) { + + return; + } + var _self = this; + // use a timeout, so that these protocols may override default methods + // inherited from http.js and the like. + $setTimeout(function() {_self.implement(apf[value]);}) + }; + + this.$propHandlers["type"] = function(value) { + this.$useXml = (typeof value == "string" && value.toUpperCase() == "XML"); + }; + + /** + * Sets the callback for a method on this object. + * Example: + * + * comm.setCallback("login", function(data, state, extra) { + * alert(data); + * }); + * + * comm.login(user, pass); + * + * @param {String} name the name of the method defined on this object. + * @param {Function} func the function that is called when the rpc method returns. + */ + this.setCallback = function(name, func){ + + + this.$methods[name].callback = func; + }; + + /** + * Sets the target url for a method on this object. + * Example: + * + * comm.setCallback("login", "scripts/login.php"); + * + * comm.login(user, pass); + * + * @param {String} name the name of the method defined on this object. + * @param {String} url the target url of method defined on this object. + */ + this.setUrl = function(name, url) { + + + this.$methods[name].setProperty("url", url); + }; + + this.$convertArgs = function(name, args){ + if (!this.namedArguments) + return Array.prototype.slice.call(args); + + var nodes = this.$methods[name].names; + if (!nodes || !nodes.length) + return {}; + + var value, j, i, l, result = {}; + for (j = 0, i = 0, l = nodes.length; i < l; i++) { + name = nodes[i].name; + value = nodes[i].value; + + if (value) { + value = apf.parseExpression(value); + } + else { + value = args[j++]; + + if (apf.isNot(value) && nodes[i]["default"]) + value = apf.parseExpression(nodes[i]["default"]); + } + + //Encode string optionally + value = apf.isTrue(nodes[i].encoded) + ? encodeURIComponent(value) + : value; + + result[name] = value; + } + + return result; + }; + + function getCallback(node) { + var p, f; + if (typeof node.callback == "string") { + // support objects and namespaced functions + p = node.callback.split("."), + f = self[p.shift()]; + while (f && p.length) + f = f[p.shift()]; + } + else { + f = node.callback; + } + return f || apf.K; + } + + this.call = function(name, args, options){ + var callback, + node = this.$methods[name]; + + if (typeof args[args.length - 1] == "function") { + args = Array.prototype.slice.call(args); //@todo optimize? + callback = args.pop(); + } + else { + callback = getCallback(node); + } + + args = this.$convertArgs(name, args); + + // Set up multicall + if (this.multicall) { + if (!this.stack[this.url]) + this.stack[this.url] = this.getMulticallObject + ? this.getMulticallObject() + : []; + + this.getSingleCall(name, args, this.stack[this.url]) + return true; + } + + // Get Data + var _self = this, + data = options && options.message + ? options.message + : this.createMessage(node["method-name"] || name, args); //function of module + + function pCallback(data, state, extra){ + extra.data = data; + + if (state != apf.SUCCESS) + callback.call(_self, null, state, extra); + else if (_self.isValid && !_self.isValid(extra)) + callback.call(_self, null, apf.ERROR, extra); + else + callback.call(_self, _self.unserialize(extra.data), state, extra); + } + + // Send the request + var auth, + url = apf.getAbsolutePath(this.baseurl || apf.config.baseurl, this.url), + o = apf.extend({ + callback : pCallback, + async : node.async, + userdata : node.userdata, + nocache : (this.nocache === false) ? false : true, + data : data, + useXML : this.$useXml || node.type == "xml", + caching : node.caching, + ignoreOffline : node["ignore-offline"] + }, options); + + + //@todo this shouldn't be in here + if (node.auth && this.$auth) { + if (auth = this.$auth.$credentials) { + o.username = auth.username; + o.password = auth.password; + } + else { + return this.$auth.authRequired(function() { + auth = _self.$auth.$credentials + o.username = auth.username; + o.password = auth.password; + _self.$get(url, o); + }); + } + } + + + return this.$get(url, o); + }; + + /** + * Purge multicalled requests + */ + this.purge = function(callback, userdata, async, extradata){ + + + // Get Data + var data = this.createMessage("multicall", [this.stack[this.url]]), //function of module + url = apf.getAbsolutePath(this.baseurl || apf.config.baseurl, this.url); + if (extradata) { + for (var vars = [], i = 0; i < extradata.length; i++) { + vars.push(encodeURIComponent(extradata[i][0]) + "=" + + encodeURIComponent(extradata[i][1] || "")) + } + url = url + (url.match(/\?/) ? "&" : "?") + vars.join("&"); + } + + var info = this.$get(url, { + callback : callback, + async : async, + userdata : userdata, + nocache : true, + data : data, + useXML : this.$useXml + }); + + this.stack[this.url] = this.getMulticallObject + ? this.getMulticallObject() + : []; + + //return info[1]; + }; + + this.revert = function(modConst){ + this.stack[modConst.url] = this.getMulticallObject + ? this.getMulticallObject() + : []; + }; + + this.getStackLength = function(){ + return this.stack[this.url] ? this.stack[this.url].length : 0; + }; + + /** + * Loads aml definition + */ + this.$addMethod = function(amlNode){ + if (amlNode.localName != "method"){ + + return false; + } + + var name = amlNode.name, + cb = amlNode.receive || this.receive, + i = 0, + l = amlNode.childNodes.length, + node; + + this[name] = function(){ + return this.call(name, arguments); + }; + + if (cb) + amlNode.callback = cb; + + this.$methods[name] = amlNode; + + if (!amlNode.names) + amlNode.names = []; + for (; i < l; i++) { + node = amlNode.childNodes[i]; + if (node.localName == "param" || node.localName == "variable") //@todo deprecate variable + amlNode.names.push(node); + } + + return true; + }; + + this.$removeMethod = function(amlNode) { + var name = amlNode.name; + delete this[name]; + delete this.$methods[name]; + }; + + this.$setAuth = function(amlNode) { + this.$auth = amlNode; + }; + + /* + this.addEventListener("DOMNodeInserted", function(e){ + var node = e.currentTarget; + if (node.parentNode != this) + return; + + this.register(node); + }); + + this.addEventListener("DOMNodeRemoved", function(e){ + var node = e.currentTarget; + // we support two levels deep: + if (!(node.parentNode == this || node.parentNode.parentNode == this)) + return; + + this.unregister(node); + });*/ + + + this.exec = function(method, args, callback, options){ + if (!options) options = {}; + + //force multicall if needed; + if (options.multicall) + this.forceMulticall = true; + + //Set information later neeed + + + var props = this.$methods[method]; + + if (options.userdata) + props.userdata = options.userdata; + + if (!this.multicall) + props.callback = callback; //&& this[method].async + + //Call method + var retvalue = this.call(method, args, options); + + if (this.multicall) + return this.purge(callback, "&@^%!@"); //Warning!! @todo Make multicall work with offline + else if (options.multicall) { + this.forceMulticall = false; + return this; + } + + + + //Call callback for sync calls + if (!this.multicall && !props.async && callback) + callback(retvalue, apf.SUCCESS, {tpModule: this}); + }; + + + /* + * Post a form with ajax + * + * @param form form + * @param function callback Called when http result is received + * / + this.submitForm = function(form, callback, callName) { + this.addMethod('postform', callback); + this.urls['postform'] = form.action; + var args = []; + for (var i = 0; i < form.elements.length; i++) { + var name = form.elements[i].name.split("["); + for(var j = 0; j < name.length; j++) { + //Hmm problem with sequence of names... have to get that from the variable sequence... + } + args[] = form.elements[i].value; + } + + this['postform'].apply(this, args); + }; + */ +}).call(apf.rpc.prototype = new apf.Teleport()); + +apf.config.$inheritProperties["baseurl"] = 1; + +apf.aml.setElement("rpc", apf.rpc); + + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/elements/script.js)SIZE(3679)TIME(Fri, 14 Oct 2011 08:01:44 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + + +/** + * element that loads javascript into the application + * either from it's first child or from a file. + * Example: + * + * + * + * Example: + * + * // + * + * @attribute {String} src the location of the script file. + * @addnode global, anyaml + * + * @author Ruben Daniels (ruben AT ajax DOT org) + * @version %I%, %G% + * @since 0.4 + */ +apf.script = function(){ + this.$init("script", apf.NODE_HIDDEN); +}; + +(function(){ + this.$propHandlers["src"] = function(value){ + if (!this.type) + this.type = this.getAttribute("type"); + + if (!this.type || this.type == "text/javascript") { + if (apf.isOpera) { + $setTimeout(function(){ + apf.window.loadCodeFile(apf.hostPath + + value); + }, 1000); + } + else { + apf.window.loadCodeFile(apf.getAbsolutePath(apf.hostPath, + value)); + } + } + else { + var _self = this; + apf.ajax(value, {callback: function(data, state, extra){ + if (state != apf.SUCCESS) { + return apf.console.warn("Could not load script " + value); + } + + _self.$execute(data); + }}); + } + } + + this.$execute = function(code, e){ + if (!this.type || this.type == "text/javascript") { + apf.jsexec(code); + } + else if (this.type.indexOf("livemarkup") > -1 + || this.type.indexOf("lm") > -1) { //@todo this is wrong, it should start in code mode + var func = apf.lm.compile(code, {event: true, parsecode: true, funcglobal: true, nostring: true}); + func(e || {}); + } + } + + this.addEventListener("DOMNodeInserted", function(e){ + if (e.currentTarget.nodeType == 3 || e.currentTarget.nodeType == 4) { + this.$execute(e.currentTarget.nodeValue, apf.isIE && window.event); + } + }); + + //@todo this should use text node insertion + this.addEventListener("DOMNodeInsertedIntoDocument", function(e){ + var nodes = this.childNodes, s = []; + for (var i = 0, l = nodes.length; i < l; i++) { + s[s.length] = nodes[i].nodeValue; + } + + var code = s.join("\n"); + + this.$execute(code); + }); +}).call(apf.script.prototype = new apf.AmlElement()); + +apf.aml.setElement("script", apf.script); + + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/elements/scrollbar.js)SIZE(26952)TIME(Tue, 06 Dec 2011 16:22:54 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + + +//@todo: fix the stuff with all the uppercase variable and function names...wazzup? + +/** + * This library needs to be refactored. + * @constructor + * @private + */ +apf.scrollbar = function(struct, tagName){ + this.$init(tagName || "scrollbar", apf.NODE_VISIBLE, struct); +}; + +(function(){ + this.realtime = true; + this.visible = false; + this.$visible = true; + this.overflow = "scroll"; + + this.$scrollSizeValue = 0; + this.$stepValue = 0.03; + this.$bigStepValue = 0.1; + this.$curValue = 0; + this.$timer = null; + this.$scrollSizeWait; + this.$slideMaxSize; + + this.$booleanProperties = ["showonscroll"]; + + this.addEventListener("focus", function(){ + if (this.$host.focus && this.$host.$isWindowContainer !== true) + this.$host.focus(); + }); + + this.$propHandlers["showonscroll"] = function(value){ + if (value) { + this.$ext.style.display = "none"; + } + else { + this.$ext.style.display = "block"; + } + }; + + this.$propHandlers["overflow"] = function(value){ + if (this.showonscroll) + return; + + if (value == "auto") { + this.$ext.style.display = "none"; + this.$resize(); + } + else if (value == "scroll") { + this.setProperty("visible", true); + } + } + + this.$propHandlers["for"] = function(value){ + if (value) { + var amlNode = typeof value == "string" ? self[value] : value; + if (!amlNode || !amlNode.$amlLoaded) { + var _self = this; + apf.queue.add("scrollbar" + this.$uniqueId, function(){ + if (!amlNode) { + amlNode = typeof value == "string" ? self[value] : value; + + if (!amlNode) { + throw new Error(apf.formatErrorString(0, _self, + "Attaching scrollbar to element", + "Could not find element to attach scrollbar to: " + value)); + } + } + _self.$attach(amlNode); + }); + } + else + this.$attach(amlNode); + } + } + + this.addEventListener("prop.visible", function(e){ + if (!this.$updating) { + this.$visible = e.value; + } + }); + + this.$detach = function(){ + + } + + //@deprecated + this.attach = function(oHtml, o, scroll_func){ + this.$attach(o); + this.addEventListener("scroll", scroll_func); + } + + this.$getHtmlHost = function(){ + var h = this.$host && (this.$host.$int || this.$host.$container); + return (h && (h.tagName == "BODY" || h.tagName == "HTML") ? (apf.isSafari || apf.isChrome ? document.body : h.parentNode) : h); + } + + this.$getViewPort = function(oHtml){ + return oHtml.tagName == "HTML" || oHtml.tagName == "BODY" ? apf[this.$windowSize]() : oHtml[this.$offsetSize]; + } + this.$getScrollHeight = function(oHtml){ + //add margin + bottom padding + return (apf.isIE && oHtml.lastChild + ? oHtml.lastChild[this.$offsetPos] + + oHtml.lastChild[this.$offsetSize] + + apf.getBox(apf.getStyle(oHtml, "padding"))[2] + + (parseInt(apf.getStyle(oHtml, "marginBottom")) || 0) + : oHtml[this.$scrollSize]); + } + + //oHtml, o, scroll_func + this.$attach = function(amlNode){ + if (!amlNode) + return apf.console.warn("Scrollbar could not connect to amlNode"); + + if (amlNode.host) + amlNode = amlNode.host; + + if (!amlNode.nodeFunc && amlNode.style) { + this.$host = { + empty : true, + $int : amlNode + }; + } + else { + this.$host = amlNode; + } + + //oHtml.parentNode.appendChild(this.$ext); + //if (this.overflow == "scroll") { + // this.$ext.style.display = "block"; + // this.enable(); + //} + + //this.$ext.style.zIndex = 100000; + //this.$ext.style.left = "166px";//(o.offsetLeft + o.offsetWidth) + "px"; + //this.$ext.style.top = "24px";//o.offsetTop + "px"; + //this.$ext.style.height = "160px";//o.offsetHeight + "px"; + + this.$recalc(); + + //this.$viewheight / this.$scrollSizeheight + //if (o.length) { + // this.$caret.style.height = Math.max(5, ((o.limit / o.length) + // * this.$slideMaxSize)) + "px"; + // if (this.$caret.offsetHeight - 4 == this.$slideMaxSize) + // this.$ext.style.display = "none"; + //} + + var scrollFunc = function(e){ + if (e.returnValue === false) + return; + + scrolling = apf.isIE; + var oHtml = _self.$getHtmlHost(); + + var div = (_self.$getScrollHeight(oHtml) - _self.$getViewPort(oHtml)); + if (div) { + if (oHtml[_self.$scrollPos] == 0 && e.delta > 0) { + if (_self.$lastScrollState === 0) + return; + setTimeout(function(){_self.$lastScrollState = 0;}, 300); + } + else if (oHtml[_self.$scrollPos] == _self.$getScrollHeight(oHtml) - oHtml[_self.$offsetSize] && e.delta < 0) { + if (_self.$lastScrollState === 1) + return; + setTimeout(function(){_self.$lastScrollState = 1;}, 300); + } + delete _self.$lastScrollState; + _self.$curValue = (oHtml[_self.$scrollPos] + -1 * e.delta * Math.min(45, apf[_self.$getInner](oHtml)/10)) / div; + _self.setScroll(null, null, null, true); + e.preventDefault(); + } + }; + + var _self = this, scrolling; + if (!this.$host.empty) { + amlNode.addEventListener("resize", function(){ //@todo cleanup? + _self.$update(); + }); + if (amlNode.hasFeature(apf.__DATABINDING__)) { + amlNode.addEventListener("afterload", function(){ + _self.$update(); + }); + amlNode.addEventListener("xmlupdate", function(){ + _self.$update(); + }); + } + + amlNode.addEventListener("prop.value", function(){ + _self.$update(); + }); + + if (amlNode.$isTreeArch) { + amlNode.addEventListener("collapse", function(){ + _self.$update(); + }); + amlNode.addEventListener("expand", function(){ + _self.$update(); + }); + } + + if (!this.horizontal) + amlNode.addEventListener("mousescroll", scrollFunc); + } + else { + if (!this.horizontal) { + apf.addEventListener("mousescroll", function(e){ + if (amlNode == e.target || (amlNode == document.documentElement && e.target == document.body)) + scrollFunc(e); + }) + } + } + + var oHtml = _self.$getHtmlHost(); + apf.addListener(oHtml, "scroll", function(){ + if (_self.animating || !_self.$visible) + return; + + if (!scrolling) { + var oHtml = _self.$getHtmlHost(); + var m = _self.$getScrollHeight(oHtml) - _self.$getViewPort(oHtml); + var p = oHtml[_self.$scrollPos] / m; + if (Math.abs(_self.$curValue - p) > 1/m) { + _self.$curValue = p; + _self.setScroll(null, null, null, true); + } + return false; + } + scrolling = false; + }); + + if ("HTML|BODY".indexOf(oHtml.tagName) > -1) { + var lastHeight = oHtml.scrollHeight; + setInterval(function(){ + if (lastHeight != oHtml.scrollHeight) { + lastHeight = oHtml.scrollHeight; + _self.$recalc(); + _self.$update(); + //_self.setScroll(null, true);*/ + } + }, 100); + } + + this.$update(); + + return this; + }; + + this.$resize = function(){ + var oHtml = this.$getHtmlHost(); + if (!oHtml || !oHtml.offsetHeight) + return; + + this.$recalc(); + this.$update(); + this.setScroll(null, true, true); + } + + this.$recalc = function(){ + var oHtml = this.$getHtmlHost(); + if (!oHtml) return; + + this.$viewheight = this.$getViewPort(oHtml); + this.$scrollSizeheight = this.$viewheight; + this.$scrollSizeWait = 0;//(this.$host.len * COLS)/2; + this.$stepValue = (this.$viewheight / this.$scrollSizeheight) / 20; + this.$bigStepValue = this.$stepValue * 3; + this.$slideMaxSize = this.$caret.parentNode[this.$offsetSize] + - (this.$btnDown ? this.$btnDown[this.$offsetSize] : 0) + - (this.$btnUp ? this.$btnUp[this.$offsetSize] : 0); + } + + //@todo this function is called way too many times + this.$update = function(){ + // Commented this out because otherwise a tree expansion wouldn't + // show the scrollbar again + //if (this.animating || !this.$visible) + // return; + + if (this.showonscroll && !this.$ext.offsetHeight) + return; + + var oHtml = this.$getHtmlHost(); + if (!oHtml || !oHtml.offsetHeight) //@todo generalize this to resize for non-ie + return; + + this.$updating = true; + + //Disable scrollbar + var vp = this.$getViewPort(oHtml); + var sz = this.$getScrollHeight(oHtml);//this.$getScrollHeight(oHtml); + + if (vp >= sz) { + if (this.overflow == "scroll") { + this.$caret.style.display = "none"; + this.disable(); + } + else if (this.visible) { + this.hide(); + + //this.$ext.style.display = "none"; + } + //if (this.id == "sbtest") console.log(vp + ":" + sz); + //oHtml.style.overflowY = "visible"; + } + //Enable scrollbar + else { + if (this.overflow == "scroll") { + this.$caret.style.display = "block"; + this.enable(); + } + else if (!this.visible) { + this.show(); + //this.$ext.style.display = "block"; + //this.$caret.style.display = "block"; + } + + if (!this.$slideMaxSize) + this.$recalc(); + if (!this.$slideMaxSize) + return; + + //oHtml.style.overflowY = "scroll"; + + //Set scroll size + this.$caret.style[this.$size] = (Math.max(5, (vp / sz + * this.$slideMaxSize)) - apf[this.$getDiff](this.$caret)) + "px"; + //if (this.$caret.offsetHeight - 4 == this.$slideMaxSize) + //this.$ext.style.display = "none"; + + this.$curValue = oHtml[this.$scrollPos] / (sz - vp); + + var bUpHeight = this.$btnUp ? this.$btnUp[this.$offsetSize] : 0; + this.$caret.style[this.$pos] = (bUpHeight + (apf[this.$getInner](this.$caret.parentNode) + - (bUpHeight * 2) - this.$caret[this.$offsetSize]) * this.$curValue) + "px"; + } + + this.$updating = false; + } + + this.setScroll = function (timed, noEvent, noUpdateParent, byUser){ + if (this.$curValue > 1) + this.$curValue = 1; + if (this.$curValue < 0) + this.$curValue = 0; + + if (this.$curValue == NaN) { + + return; + } + + var bUpHeight = this.$btnUp ? this.$btnUp[this.$offsetSize] : 0; + this.$caret.style[this.$pos] = (bUpHeight + (apf[this.$getInner](this.$caret.parentNode) + - (bUpHeight * 2) - this.$caret[this.$offsetSize]) * this.$curValue) + "px"; + + if (this.animating || !this.$visible) + return; + + var oHtml, from, viewport, to; + if (this.$host) { + oHtml = this.$getHtmlHost(); + from = oHtml[this.$scrollPos]; + viewport = this.$getViewPort(oHtml); + to = (this.$getScrollHeight(oHtml) - viewport) * this.$curValue; + } + + if (!noUpdateParent) { + if (this.$host) + oHtml[this.$scrollPos] = to; + } + + if (!noEvent) { + (this.$host && this.$host.dispatchEvent + ? this.$host + : this).dispatchEvent("scroll", { + timed : timed, + viewportSize : viewport, + scrollPos : to, + scrollSize : this.$getScrollHeight(oHtml), + from : from, + pos : this.pos + }); + } + + if (this.showonscroll && byUser) { + var _self = this; + + clearTimeout(this.$hideOnScrollTimer); + if (_self.$hideOnScrollControl) + _self.$hideOnScrollControl.stop(); + + apf.setOpacity(this.$ext, 1); + !this.visible ? this.show() : this.$ext.style.display = "block"; + this.$update(); + + this.$hideOnScrollTimer = setTimeout(function(){ + apf.tween.single(_self.$ext, { + control : _self.$hideOnScrollControl = {}, + type : "fade", + from : 1, + to : 0, + onfinish : function(){ + _self.$ext.style.display = "none"; + apf.setOpacity(_self.$ext, 1); + } + }); + }, 500) + } + + this.pos = this.$curValue; + } + + this.scrollUp = function (v){ + if (v > this.$caret[this.$offsetPos]) + return this.$ext.onmouseup(); + this.$curValue -= this.$bigStepValue; + this.setScroll(null, null, null, true); + + if (this.$slideFast) { + this.$slideFast.style[this.$size] = Math.max(1, this.$caret[this.$offsetPos] + - this.$btnUp[this.$offsetSize]) + "px"; + this.$slideFast.style[this.$pos] = this.$btnUp[this.$offsetSize] + "px"; + } + } + + this.scrollDown = function (v){ + if (v < this.$caret[this.$offsetPos] + this.$caret[this.$offsetSize]) + return this.$ext.onmouseup(); + this.$curValue += this.$bigStepValue; + this.setScroll(null, null, null, true); + + if (this.$slideFast) { + this.$slideFast.style[this.$pos] = (this.$caret[this.$offsetPos] + this.$caret[this.$offsetSize]) + "px"; + this.$slideFast.style[this.$size] = Math.max(1, apf[this.$getInner](this.$caret.parentNode) - this.$slideFast[this.$offsetPos] + - this.$btnUp[this.$offsetSize]) + "px"; + } + } + + this.getPosition = function(){ + return this.pos; + }; + + this.setPosition = function(pos, noEvent){ + this.$curValue = pos; + this.setScroll(null, noEvent); + }; + + this.updatePos = function(){ + if (this.animating || !this.$visible) + return; + + var o = this.$host; + var indHeight = Math.round(Math.max(10, (((o.limit - 1) / o.length) * this.$slideMaxSize))); + this.$caret.style[this.$pos] = (this.$curValue * (this.$slideMaxSize - indHeight) + this.$btnUp[this.$offsetSize]) + "px"; + } + + this.$onscroll = function(timed, perc){ + this.$host[this.$scrollPos] = (this.$host[this.$scrollSize] - this.$host[this.$offsetSize] + 4) * this.$curValue; + } + + this.$draw = function(){ + //Build Skin + this.$getNewContext("main"); + this.$ext = this.$getExternal(); + //this.$ext.style.display = "none"; + + this.$caret = this.$getLayoutNode("main", "indicator", this.$ext); + this.$slideFast = this.$getLayoutNode("main", "slidefast", this.$ext); + this.$btnUp = this.$getLayoutNode("main", "btnup", this.$ext) + this.$btnDown = this.$getLayoutNode("main", "btndown", this.$ext); + + this.horizontal = apf.isTrue(this.$getOption("main", "horizontal")); + + this.$windowSize = this.horizontal ? "getWindowWidth" : "getWindowHeight"; + this.$offsetSize = this.horizontal ? "offsetWidth" : "offsetHeight"; + this.$size = this.horizontal ? "width" : "height"; + this.$offsetPos = this.horizontal ? "offsetLeft" : "offsetTop"; + this.$pos = this.horizontal ? "left" : "top"; + this.$scrollSize = this.horizontal ? "scrollWidth" : "scrollHeight"; + this.$scrollPos = this.horizontal ? "scrollLeft" : "scrollTop"; + this.$getDiff = this.horizontal ? "getWidthDiff" : "getHeightDiff"; + this.$getInner = this.horizontal ? "getHtmlInnerWidth" : "getHtmlInnerHeight"; + this.$eventDir = this.horizontal + ? (apf.isIE || apf.isWebkit ? "offsetX" : "layerX") + : (apf.isIE || apf.isWebkit ? "offsetY" : "layerY"); + this.$clientDir = this.horizontal ? "clientX" : "clientY"; + this.$posIndex = this.horizontal ? 0 : 1; + + this.$startPos = false; + + this.$caret.ondragstart = function(){ + return false + }; + + var _self = this; + if (this.$btnUp) { + this.$btnUp.onmousedown = function(e){ + if (_self.disabled) + return; + + if (!e) + e = event; + this.className = "btnup btnupdown"; + clearTimeout(_self.$timer); + + _self.$curValue -= _self.$stepValue; + + _self.setScroll(null, null, null, true); + apf.stopPropagation(e); + + //apf.window.$mousedown(e); + + _self.$timer = $setTimeout(function(){ + _self.$timer = setInterval(function(){ + _self.$curValue -= _self.$stepValue; + _self.setScroll(null, null, null, true); + }, 20); + }, 300); + }; + + this.$btnUp.onmouseout = this.$btnUp.onmouseup = function(){ + if (_self.disabled) + return; + + this.className = "btnup"; + clearInterval(_self.$timer); + }; + } + + if (this.$btnDown) { + this.$btnDown.onmousedown = function(e){ + if (_self.disabled) + return; + + if (!e) + e = event; + this.className = "btndown btndowndown"; + clearTimeout(_self.$timer); + + _self.$curValue += _self.$stepValue; + _self.setScroll(null, null, null, true); + apf.stopPropagation(e); + + //apf.window.$mousedown(e); + + _self.$timer = $setTimeout(function(){ + _self.$timer = setInterval(function(){ + _self.$curValue += _self.$stepValue; + _self.setScroll(null, null, null, true); + }, 20); + }, 300); + }; + + this.$btnDown.onmouseout = this.$btnDown.onmouseup = function(){ + if (_self.disabled) + return; + + this.className = "btndown"; + clearInterval(_self.$timer); + }; + } + + this.$caret.onmousedown = function(e){ + if (_self.disabled) + return; + + if (!e) + e = event; + + var tgt = e.target || e.srcElement; + var pos = tgt != this + ? [tgt.offsetLeft, tgt.offsetTop] //Could be improved + : [0, 0]; + + var relDelta = e[_self.$eventDir] + pos[_self.$posIndex]; + _self.$startPos = relDelta + + (_self.$btnUp ? _self.$btnUp[_self.$offsetSize] : 0); + + if (this.setCapture) + this.setCapture(); + + _self.$setStyleClass(_self.$ext, _self.$baseCSSname + "Down"); + _self.dispatchEvent("mousedown", {}); + + document.onmousemove = function(e){ + if (!e) + e = event; + //if(e.button != 1) return _self.onmouseup(); + if (_self.$startPos === false) + return false; + + var bUpHeight = _self.$btnUp ? _self.$btnUp[_self.$offsetSize] : 0; + var next = bUpHeight + (e[_self.$clientDir] - _self.$startPos + + (apf.isWebkit ? document.body : document.documentElement)[_self.$scrollPos] + - apf.getAbsolutePosition(_self.$caret.parentNode)[_self.horizontal ? 0 : 1]); // - 2 + var min = bUpHeight; + if (next < min) + next = min; + var max = (apf[_self.$getInner](_self.$caret.parentNode) + - bUpHeight - _self.$caret[_self.$offsetSize]); + if (next > max) + next = max; + //_self.$caret.style.top = next + "px" + + _self.$curValue = (next - min) / (max - min); + _self.setScroll(true); + }; + + document.onmouseup = function(){ + _self.$startPos = false; + if (!_self.realtime) + _self.setScroll(); + + if (this.releaseCapture) + this.releaseCapture(); + + _self.$setStyleClass(_self.$ext, "", [_self.$baseCSSname + "Down"]); + _self.dispatchEvent("mouseup", {}); + document.onmouseup = + document.onmousemove = null; + }; + + apf.stopPropagation(e); + //apf.window.$mousedown(e); + + return false; + }; + + this.$ext.onmousedown = function(e){ + if (_self.disabled) + return; + if (!e) + e = event; + clearInterval(_self.$timer); + var offset; + if (e[_self.$eventDir] > _self.$caret[_self.$offsetPos] + _self.$caret[_self.$offsetSize]) { + _self.$curValue += _self.$bigStepValue; + _self.setScroll(true, null, null, true); + + if (_self.$slideFast) { + _self.$slideFast.style.display = "block"; + _self.$slideFast.style[_self.$pos] = (_self.$caret[_self.$offsetPos] + + _self.$caret[_self.$offsetSize]) + "px"; + _self.$slideFast.style[_self.$size] = (apf[_self.$getInner](_self.$caret.parentNode) - _self.$slideFast[_self.$offsetPos] + - _self.$btnUp[_self.$offsetSize]) + "px"; + } + + offset = e[_self.$eventDir]; + _self.$timer = $setTimeout(function(){ + _self.$timer = setInterval(function(){ + _self.scrollDown(offset, null, null, true); + }, 20); + }, 300); + } + else if (e[_self.$eventDir] < _self.$caret[_self.$offsetPos]) { + _self.$curValue -= _self.$bigStepValue; + _self.setScroll(true, null, null, true); + + if (_self.$slideFast) { + _self.$slideFast.style.display = "block"; + _self.$slideFast.style[_self.$pos] = _self.$btnUp[_self.$offsetSize] + "px"; + _self.$slideFast.style[_self.$size] = (_self.$caret[_self.$offsetPos] - _self.$btnUp[_self.$offsetSize]) + "px"; + } + + offset = e[_self.$eventDir]; + _self.$timer = $setTimeout(function(){ + _self.$timer = setInterval(function(){ + _self.scrollUp(offset, null, null, true); + }, 20); + }, 300); + } + }; + + this.$ext.onmouseup = function(){ + if (_self.disabled) + return; + + clearInterval(_self.$timer); + if (!_self.realtime) + _self.setScroll(null, null, null, true); + if (_self.$slideFast) + _self.$slideFast.style.display = "none"; + }; + + this.$ext.onmouseover = function(e){ + _self.dispatchEvent("mouseover", {htmlEvent : e || event}); + }; + + this.$ext.onmouseout = function(e){ + _self.dispatchEvent("mouseout", {htmlEvent : e || event}); + }; + } + + this.$loadAml = function(){ + if (this.overflow == "scroll") + this.disable(); + else { + this.$caret.style.display = "block"; + this.enable(); + } + + this.addEventListener("resize", this.$resize); + this.$update(); + } +}).call(apf.scrollbar.prototype = new apf.Presentation()); +apf.aml.setElement("scrollbar", apf.scrollbar); + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/elements/services.js)SIZE(1488)TIME(Fri, 14 Oct 2011 08:01:44 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + + +/** + * + * @author Ruben Daniels (ruben AT ajax DOT org) + * @version %I%, %G% + * @since 0.4 + */ +apf.services = function(struct, tagName){ + this.$init(tagName || "services", apf.NODE_VISIBLE, struct); + + this.addEventListener("DOMNodeInsertedIntoDocument", function(aml){ + var pNode = this.parentNode; + if (pNode.register) + pNode.register(this); + }); +}; + +apf.services.prototype = new apf.AmlElement(); +apf.aml.setElement("services", apf.services); + + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/elements/skin.js)SIZE(9690)TIME(Tue, 13 Dec 2011 13:33:58 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + +/** + * element specifying the skin of an application. + * Example: + * + * + * + * @attribute {String} name the name of the skinset. + * @attribute {String} src the location of the skin definition. + * @attribute {String} media-path the basepath for the images of the skin. + * @attribute {String} icon-path the basepath for the icons used in the elements using this skinset. + * @allowchild style, presentation + * @addnode global, anyaml + * + * @author Ruben Daniels (ruben AT ajax DOT org) + * @version %I%, %G% + * @since 0.4 + */ +apf.skin = function(struct, tagName){ + this.$init(tagName || "skin", apf.NODE_HIDDEN, struct); +}; +apf.aml.setElement("skin", apf.skin); + +(function(){ + this.$parsePrio = "002"; + this.$includesRemaining = 0; + + this.$propHandlers["src"] = function(value){ + if (value.trim().charAt(0) == "<") { + apf.skins.Init(apf.getXml(value), this, this.$path); + return; + } + + this.$path = apf.getAbsolutePath(apf.hostPath, value) + getSkin.call(this, this.$path); + } + + this.$propHandlers["name"] = function(value){ + if (!this.attributes.getNamedItem("src")) { + this.$path = apf.getAbsolutePath(apf.hostPath, value) + "/index.xml"; + getSkin.call(this, this.$path); + } + } + + /** + * @private + */ + function checkForAmlNamespace(xmlNode){ + if (!xmlNode.ownerDocument.documentElement) + return false; + + var nodes = xmlNode.ownerDocument.documentElement.attributes; + for (var found = false, i=0; i' + xmlString + ''); + apf.mergeXml(newPart, xmlNode, {beforeNode: includeNode}); + includeNode.parentNode.removeChild(includeNode); + + var includeNodes = $xmlns(newPart, "include", apf.ns.aml); + _self.$includesRemaining += includeNodes.length; + if (includeNodes.length) { + var path = apf.getDirname(extra.url); + for (var i = 0; i < includeNodes.length; i++) { + loadSkinInclude.call(_self, includeNodes[i], xmlNode, path); + } + } + else if (--_self.$includesRemaining == 0) { + + + finish.call(_self, xmlNode); + } + } + }); + } + + + function loadSkinFile(path){ + + + var _self = this; + + apf.getData( + + path, { + + callback: function(xmlString, state, extra){ + if (state != apf.SUCCESS) { + var oError = new Error(apf.formatErrorString(1007, + _self, "Loading skin file", "Could not load skin file '" + + (path || _self.src) + + "'\nReason: " + extra.message)); + + if (extra.tpModule.retryTimeout(extra, state, null, oError) === true) + return true; + + + if (this.autoload) { + apf.console.warn("Could not autload skin."); + return finish.call(_self); + } + + + throw oError; + } + + //if (!apf.supportNamespaces) + xmlString = xmlString.replace(/\<\!DOCTYPE[^>]*>/, "") + .replace(/^[\r\n\s]*/, "") //.replace(/ /g, " ") + .replace(/xmlns\=\"[^"]*\"/g, ""); + + if (!xmlString) { + throw new Error(apf.formatErrorString(0, _self, + "Loading skin", + "Empty skin file. Maybe the file does not exist?", _self)); + } + + var xmlNode = apf.getXml(xmlString);//apf.getAmlDocFromString(xmlString); + + + + if (!xmlNode) { + throw new Error(apf.formatErrorString(0, _self, + "Loading skin", + "Could not parse skin. Maybe the file does not exist?", _self)); + } + + xmlNode.setAttribute("filename", extra.url); + + + var includeNodes = $xmlns(xmlNode, "include", apf.ns.aml); + _self.$includesRemaining += includeNodes.length; + if (includeNodes.length) { + var path = apf.getDirname(extra.url); + for (var i = 0; i < includeNodes.length; i++) { + loadSkinInclude.call(_self, includeNodes[i], xmlNode, path); + } + return; + } + else + + { + + + finish.call(_self, xmlNode); + } + }, + async : true, + ignoreOffline : true + }); + } + + //@todo use mutation events to update + this.addEventListener("DOMNodeInsertedIntoDocument", function(e){ + if (this.src || this.name) + return; + + apf.skins.Init(this.$aml || this); + + //@todo implied skin + /*if (this.parentNode && this.parentNode.parentNode) { + var name = "skin" + Math.round(Math.random() * 100000); + q.parentNode.setAttribute("skin", name); + apf.skins.skins[name] = {name: name, templates: {}}; + apf.skins.skins[name].templates[q.parentNode[apf.TAGNAME]] = q; + }*/ + }); +}).call(apf.skin.prototype = new apf.AmlElement()); + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/elements/slider.js)SIZE(32341)TIME(Tue, 06 Dec 2011 16:22:54 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/elements/slideshow.js)SIZE(47089)TIME(Fri, 14 Oct 2011 08:01:44 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/elements/smartbinding.js)SIZE(33619)TIME(Fri, 14 Oct 2011 08:01:44 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + + +/** + * @term smartbinding Smartbinding is a type of bidirectional databinding where + * rules specify how data is rendered in a component and how changes to + * the bound data are sent back to the server (or other data source). + * Smartbindings are specifically designed to solve the problem of databinding + * for Ajax applications that connect to remote (non-local) datasources. + * A smartbinding element can contain three elements; {@link element.bindings bindings}, + * {@link element.actions actions} and {@link element.model model}. + * + * See also more information about {@link term.binding binding rules} and + * {@link term.action action rules}. + * + * Model: + * The model is the place where your xml data resides. Data is loaded into the + * model using a {@link term.datainstruction data instruction} as the following + * example shows: + * + * + * + * An element can connect directly to a model in order to bind to data. + * + * + * + * + * + * The model can also be part of a smartbinding that is used by the element. + * A smartbinding can be used by multiple elements referenced by id: + * + * + * + * + * + * + * + * + * + * + * + * + * Bindings: + * The bindings element is a container for binding rules. Binding rules determine + * how an element renders the data that it's bound to. Some binding rules specify + * how data can be interacted with (i.e. {@link baseclass.multiselect.binding.select the select rule}). + * Check the {@link term.binding term binding rules} for more information. + * + * Actions: + * The actions element is a container for action rules. Action rules influence + * and trigger several parts of the user interaction. + *
    + *
  1. It determines whether a user action can be executed on the bound and/or + * selected {@link term.datanode data node}.
  2. + *
  3. It dispatches events, before and after the data is changed.
  4. + *
  5. It creates a {@link http://en.wikipedia.org/wiki/Command_pattern command object} + * that is pushed on the undo stack of the {@link element.actiontracker actiontracker} + * connected to the element that triggered the action.
  6. + *
  7. The command object contains all the information to send the change back + * to the server
  8. + *
+ * So in short, an action rule is always triggered by the user, creates an + * undo item and sends the change back to the server. + * Check the {@link term.action term action rules} for more information. + * + * See: + * {@link baseclass.databinding.attribute.smartbinding} + */ + +/** + * @term binding Binding rules determine how an element displays the data that + * its bound to (ex.: {@link element.tree.binding.caption the caption rule}), + * and determines how it can be interacted with + * (ex.: {@link baseclass.multiselect.binding.select the select rule}). + * Binding rules are part of the {@link term.smartbinding smartbinding concept}. + * + * Basic: + * Let's take a simple example, that of a {@link element.textbox textbox}. A + * textbox has a {@link element.textbox.attribute.value value attribute}. This + * attribute can be set like this: + * + * + * + * In many cases it's handy to bind the value of the textbox to data. Imagine + * you are editing a contact's name in a textbox. In this case you would want to + * bind the value of the textbox to the xml data. The binding rule is configured + * to determine this value based on the bound xml. Let's look at an example: + * + * + * + * Test + * + * + * + * + * + * The textbox binds to the data of the model. The bind rule sets how the value + * is retrieved from the bound data. In this case the value of the name node is + * retrieved. When the user changes the value of the textbox, the name + * node is updated with that value. Subsequently when the xml + * changes the value of the textbox is updated. + * + * Each attribute on an element can be bound to data by using the attribute + * name as the name of the binding rule. In the next example, the visible + * attribute of a textbox is based on the availability of a {@link term.datanode data node}: + * + * + * + * + * + * + * + * Each element has a primary bind rule that can be accessed in a short format. + * This is usually the value bind rule. The short format works as follows: + * + * + * + * Test + * + * + * + * + * + * Advanced: + * For multi node components databinding adds another conceptual step. The basics + * stay the same, though a way is introduced to do 'foreach' on the data to + * determine which nodes are rendered. This is done using the + * {@link element.multiselectbinding.binding.each each binding rule} and + * the selected nodes are called {@link term.eachnode each nodes}. + * + * When the set of each nodes is determined, each is rendered based on other + * binding rules that determine whatever is deemed necesary by the component. + * This can be the caption, icon, tooltip, whether an item is seletable and so on. + * In the next example a list is bound to some data representing a contact list. + * Each contact's name is displayed as the caption of the item. + * + * + * + * + * Ruben + * Javeline + * + * + * Łukasz + * Javeline + * + * + * + * + * + * + * + * + * + * + * + * + * Fallbacks: + * By stacking multiple binding rules it's possible to define different ways to + * determine the value for an attribute. Let's say we have a tree that displays + * files and folders. A file and a folder can have custom icons. If these are + * not specified, they each default to an icon representing their type. This would + * be encoded like this: + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * Processors: + * There are several ways to convert the data retrieved from the xml data into + * the needed string or boolean. The following example uses {@link term.livemarkup live markup} + * to determine the icon by the extension of the filename: + * + * + * + * + * Ruben + * Baseclasses + * + * + * Łukasz + * application.png + * + * + * + * + * + * + * + * + * + * + * + * Instead of live markup you can use xslt as well. Furthermore you can apply some + * javascript to the result by calling a method. The following examples shows + * a caption where a javascript method inserts smileys. + * + * + * + * + * + * + * + * + * + * function insertSmileys(value) { + * //do something with value + * return value; + * } + * + * + * + * + * + * + * + * + * Extending: + * Two special binding rules are the {@link baseclass.databinding.binding.load load} + * and the {@link element.tree.binding.insert insert} bindings. These bindings + * are used to load and insert new data into the data bound to the element that + * uses them. With these rules an application can start out with only a bit of + * data and when the user needs it extends the data. A simple example is that of + * a tree element that loads subnodes whenever a user expands a node. This can + * be achieved in the following way: + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * For more information about how data can be loaded into aml elements please + * check {@link term.datainstruction data instructions}. + */ + +/** + * @term action Action rules determine whether a user can execute an action and + * takes care of executing the change both locally and on a remote server. Each + * triggered action creates an item on the undo stack. + * Action rules are part of the {@link term.smartbinding smartbinding concept}. + * + * Syntax: + * Actions are added to {@link element.actions}. The select attribute specifies + * whether an action can be executed. The set attribute specifies how the change + * to the data is send to the server. The following example shows a remove + * action on a datagrid. A jsp script is called to process the change. This is + * specified using a {@link term.datainstruction data instruction}. + * + * + * + * + * + * + * + * + * Defaults: + * The default behaviour for all components is to enable all actions when no + * actions element has been assigned. This can be change by setting + * {@link element.appsettings.attribute.auto-disable-actions}. When a actions + * element is assigned, all actions are disabled unless they are specified. + * When the select attribute on an action is not set the action will always be + * allowed. + * + * Flow: + * Action rules influence and trigger several parts of the user interaction. + *
    + *
  1. It determines whether a user action can be executed on the bound and/or + * selected {@link term.datanode data node}.
  2. + *
  3. It dispatches events, before and after the data is changed.
  4. + *
  5. It creates a {@link http://en.wikipedia.org/wiki/Command_pattern command object} + * that is pushed on the undo stack of the {@link element.actiontracker actiontracker} + * connected to the element that triggered the action.
  6. + *
  7. The command object ({@link core.undodata UndoData}) contains all the + * information to send the change back to the server.
  8. + *
+ * + * Fallbacks: + * By stacking multiple action rules it's possible to define different ways to + * deal with user actions. Let's say we have a tree that displays + * files and folders. Renaming a file and a folder might have different handlers. + * This would be encoded like this: + * + * + * + * + * + * + * + * + * + * + * + * Undo + * + * + * Undo: + * When an action is execute it creates an entry on the undostack of an + * actiontracker. Undo can be triggered by calling the undo method. + * + * myTree.getActionTracker().undo(); + * //or + * ActionTracker.undo(); + * + * Executing will revert the change to the data. This will also be communicated + * to the server. In some cases the call to the server is not symmetric; the set + * call cannot be used to revert. For these situations set the undo attribute. + * + * + * + * + * + * + * + * + * + * Undo + * Remove + * + * In the example above the server is required to support reverting remove. + * Another possibility is to add the item again as shown in this example: + * + * + * + * + * + * Javascript: + * Each action has a method associated with it that exists on the element that + * the action rule is assigned to. The method has the same name as the action + * and can be called from javascript. For instance, the {@link baseclass.multiselect.binding.remove remove action}: + * + * myTree.remove(); + * myTree.remove(dataNode); + * + * + * Add: + * Adding {@link term.datanode data nodes} to an element is a bit more advanced because the origin of + * the new data can be encoded in {@link baseclass.multiselect.binding.add the add action rule}. + * There are three ways to provide the data to add a node. + * + * The first is by calling the add method using javascript. + * + * + * + * + * + * myList.add(''); + * + * + * + * The second by specifying the template as a child of the add action rule: + * + * + * + * + * + * The third way gets the added node from the server. + * + * + * + * + * + * + * + * + * + * + * + * + * + * LCD Panel + * + * + * + * Add product + * + * + * Purging: + * Sometimes it's necesary to not send the changes directly to the server. For + * instance when the application offers a save button. To achieve this + * set the {@link element.actiontracker.attribute.realtime realtime attribute} + * of the actiontracker to false. The save button can call the + * {@link element.actiontracker.method.purge purge method} to have the + * actiontracker send the calls. + * + * + * + * Save + * + * N.B. At a certain amount of changes this way will become inefficient and + * you'll want to send the state of your data to the server directly. You can + * do that like this: + * + * + * + * + * + * + * + * + * + * LCD Panel + * + * + * + * + * + * + * Save + * + * + * See also {@link element.model.method.submit}. + * + * Transactions: + * A transaction is a + * set of changes to data which are treated as one change. When one of the + * changes in the set fails, all the changes will be cancelled. In the case of + * a gui this is happens when a user decides to cancel after + * making several changes. A good example are the well known Properties + * windows with an ok, cancel and apply button. + * + * When a user edits data, for instance user information, all the changes are + * seen as one edit and put on the undo stack as a single action. Thus clicking + * undo will undo the entire transaction, not just the last change done by that + * user in the edit window. Transaction support both optimistic and pessimistic + * locking. For more information on transactions see {@link baseclass.transaction}. + */ + +/** + * @term datanode A data node is the term used for any xml node (attribute, + * element, textnode or otherwise) that is used in a databound context. So when + * xml is loaded into a {@link element.model model} we refer to those xml nodes + * as data nodes. + */ + +/** + * Element containing information on how databound elements process data. + * The {@link term.smartbinding smartbinding} element specifies how data is transformed and rendered + * in databound elements. It also specifies how changes on the bound data are + * send to their original data source ({@link element.actions actions}) and + * which {@link term.datanode data nodes} can be dragged and dropped ({@link element.dragdrop dragdrop}). + * Example: + * A simple example of a smartbinding transforming data into representation + * + * + * + * + * + * + * + * + * + * + * + * + * LCD Panel + * + * + * + * + * Example: + * This is an elaborate example showing how to create a filesystem tree with + * files and folders in a tree. The smartbinding element describes how the + * files and folders are transformed to tree elements and how actions within + * the tree are sent to the data source. In this case {@link teleport.webdav WebDAV} + * is used. The drag and drop rules specify which elements can be dragged and + * where they can be dropped. + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * function filesort(value, args, xmlNode) { + * return (xmlNode.tagName == "folder" ? 0 : 1) + value; + * } + * + * function getIcon(xmlNode){ + * xmlNode.getAttribute('name').match(/\.([^\.]*)$/); + * + * var ext = RegExp.$1; + * return (SupportedIcons[ext.toUpperCase()] + * ? SupportedIcons[ext.toUpperCase()] + ".png" + * : "unknown.png"); + * } + * + * + * Remarks: + * Each element has it's own set of binding rules it uses to render the data + * elements. The same goes for it's actions. To give an example, a slider has + * one action called 'change'. This action is called when then value of the + * slider changes. A tree element has several actions - among others: 'add', + * 'remove', 'move', 'copy' and 'rename'. + * + * Smartbindings enable many other features in a Ajax.org Platform + * application. Actions done by the user can be undone by calling + * {@link element.actiontracker.method.undo} of the element. The + * Remote Databinding element can send changes on data to other clients. + * + * This element is created especially for reuse. Multiple elements can reference + * a single smartbinding element by setting the value of the 'smartbinding' + * attribute to the ID of this smartbinding element. If an element is only used + * for a single other element it can be set as it's child. In fact, each of the + * children of the smartbinding element can exist outside the smartbinding + * element and referenced indepently. + * Example: + * This example shows a smartbinding element which references to its children as + * stand alone elements. + * + * + * ... + * + * + * ... + * + * + * ... + * + * + * + * + * + * + * + * + * Example: + * The shortest method to add binding rules to an element is as follows: + * + * + * + * @see baseclass.databinding + * @see baseclass.databinding.attribute.smartbinding + * @see term.smartbinding + * @see term.binding + * @see term.action + * + * @define smartbinding + * @allowchild bindings, actions, ref, action, dragdrop, model + * @addnode smartbinding, global + * + * @constructor + * @apfclass + * + * @author Ruben Daniels (ruben AT ajax DOT org) + * @version %I%, %G% + * @since 0.8 + * + * @default_private + */ +apf.smartbinding = function(struct, tagName){ + this.$init(tagName || "smartbinding", apf.NODE_HIDDEN, struct); + + this.$bindNodes = {}; +}; + +(function(){ + this.$supportedProperties = ["bindings", "actions", "model"]; + this.$handlePropSet = function(prop, value, force){ + switch(prop) { + //@todo apf3 change this to use apf.setModel(); + case "model": + + if (typeof value == "string") + value = apf.nameserver.get("model", value); + this.model = apf.nameserver.register("model", this.name, value); + //this.modelBaseXpath = xpath; + + var amlNode; + for (var uniqueId in this.$bindNodes) { + amlNode = this.$bindNodes[uniqueId]; + this.model.unregister(amlNode); + this.model.register(amlNode, this.$modelXpath[amlNode.getHost + ? amlNode.getHost().$uniqueId + //this is a hack.. by making Models with links to other + //models possible, this should not be needed + : amlNode.$uniqueId] || this.modelBaseXpath); + //this.$bindNodes[uniqueId].load(this.model); + } + + break; + case "bindings": + if (this.$bindings) + this.remove(this.$bindings); + + this.$bindings = typeof value == "object" + ? value + : + + apf.nameserver.lookup("bindings", value); + + + this.add(this.$bindings); + + break; + case "actions": + if (this.$actions) + this.remove(this.$actions); + + this.$actions = typeof value == "object" + ? value + : + + apf.nameserver.lookup("actions", value); + + + this.add(this.$actions); + + break; + } + + this[prop] = value; + + + }; + + this.add = function(node){ + for (var uId in this.$bindNodes) + node.register(this.$bindNodes[uId]); + + this["$" + node.localName] = node; + }; + + this.remove = function(node){ + for (var uId in this.$bindNodes) + node.unregister(this.$bindNodes[uId]); + }; + + this.register = function(amlNode){ + this.$bindNodes[amlNode.$uniqueId] = amlNode; + + if (this.$bindings) + this.$bindings.register(amlNode); + if (this.$actions) + this.$actions.register(amlNode); + if (this.$model) + this.$model.register(amlNode); + }; + + this.unregister = function(amlNode){ + //unregister element + this.$bindNodes[amlNode.$uniqueId] = null; + delete this.$bindNodes[amlNode.$uniqueId]; + + if (this.$bindings) + this.$bindings.unregister(amlNode); + if (this.$actions) + this.$actions.unregister(amlNode); + if (this.$model) + this.$model.unregister(amlNode); + }; + + /** + * Loads xml data in the model of this smartbinding element. + * + * @param {mixed} xmlNode the {@link term.datanode data node} loaded into + * the model of this smartbinding element. This can be an XMLElement, a + * string or null. + * @private + */ + this.load = function(xmlNode){ + //@todo fix this + new apf.model().register(this).load(xmlNode); + }; + + this.clear = function(state){ + //for all elements do clear(state); + }; + + /** + * @private + * + * @attribute {String} bindings the id of the bindings element that contains + * the {@link term.binding binding rules} for all elements connected to + * this smartbinding element + * Example: + * + * + * + * @see element.bindings + * @see term.binding + * @see term.smartbinding + * + * @attribute {String} actions the id of the actions element that provides + * the {@link term.action action rules} for all elements connected to + * this smartbinding element + * Example: + * + * + * + * @see element.actions + * @see term.action + * @see term.smartbinding + * + * @attribute {String} dragdrop the id of the dragdrop element that provides + * the drag and drop rules for all elements connected to this smartbinding + * element + * Example: + * + * + * + * @see element.dragdrop + * @see term.smartbinding + * + * @attribute {String} model the id of the model element that provides + * the data for all elements connected to this smartbinding element. + * Example: + * + * + * + * @see element.model + * @see term.smartbinding + * + * @define bindings element containing all the binding rules for the data + * bound elements referencing this element. + * Example: + * + * + * + * + * + * + * + * + * + * @see element.smartbinding + * @allowchild {bindings} + * @addnode smartbinding, global + * @addnode smartbinding, global + */ + this.addEventListener("DOMNodeInsertedIntoDocument", function(e){ + if (this.parentNode.hasFeature(apf.__DATABINDING__)) + this.register(this.parentNode); + + + }); +}).call(apf.smartbinding.prototype = new apf.AmlElement()); + +apf.aml.setElement("smartbinding", apf.smartbinding); + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/elements/source.js)SIZE(1566)TIME(Fri, 14 Oct 2011 08:01:44 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + + +/** + * Element + * + * @constructor + * + * @define source + * @addnode audio, video + * + * @author Mike de Boer (mike AT javeline DOT com) + * @version %I%, %G% + * @since 3.0 + */ +apf.source = function(struct, tagName){ + this.$init(tagName || "source", apf.NODE_HIDDEN, struct); +}; + +(function(){ + this.$supportedProperties.push("src", "type"); + + this.addEventListener("DOMNodeInsertedIntoDocument", function(e){ + if (this.parentNode.$addSource) + this.parentNode.$addSource(this); + }); +}).call(apf.source.prototype = new apf.AmlElement()); + +apf.aml.setElement("source", apf.source); + + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/elements/spinner.js)SIZE(16965)TIME(Tue, 06 Dec 2011 16:22:54 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + +/** + * This element is used to choosing number by plus/minus buttons. + * When plus button is clicked longer, number growing up faster. The same + * situation is for minus button. It's possible to increment and decrement + * value by moving mouse cursor up or down with clicked input. Max and + * min attributes define range with allowed values. + * + * Example: + * Spinner element with start value equal 6 and allowed values from range + * (-100, 200) + * + * + * + * + * Example: + * Sets the value based on data loaded into this component. + * + * + * + * + * + * + + * Example: + * Is showing usage of model in spinner connected with textbox + * + * + * + * + * + * + * + * + * + * @attribute {Number} max maximal allowed value, default is 64000 + * @attribute {Number} min minimal allowed value, default is -64000 + * @attribute {Number} value actual value displayed in component + * + * @classDescription This class creates a new spinner + * @return {Spinner} Returns a new spinner + * + * @author + * @version %I%, %G% + * + * @inherits apf.StandardBinding + * @inherits apf.DataAction + * @inherits apf.XForms + * + * @binding value Determines the way the value for the element is retrieved + * from the bound data. + */ +apf.spinner = function(struct, tagName){ + this.$init(tagName || "spinner", apf.NODE_VISIBLE, struct); + + this.max = 64000; + this.min = -64000; + this.focused = false; + this.value = 0; + + this.realtime = false; +}; + +(function() { + this.implement( + + apf.DataAction + + + ); + + this.$supportedProperties.push("width", "value", "max", "min", "caption", "realtime"); + + this.$booleanProperties["realtime"] = true; + + this.$propHandlers["value"] = function(value) { + value = parseInt(value) || 0; + + this.value = this.oInput.value = (value > this.max + ? this.max + : (value < this.min + ? this.min + : value)); + }; + + this.$propHandlers["min"] = function(value) { + if (!(value = parseInt(value))) return; + this.min = value; + if (value > this.value) + this.change(value); + }; + + this.$propHandlers["max"] = function(value) { + if (!(value = parseInt(value))) return; + this.max = value; + + if (value < this.value) + this.change(value); + }; + + /* ******************************************************************** + PUBLIC METHODS + *********************************************************************/ + + + + /** + * Sets the value of this element. This should be one of the values + * specified in the values attribute. + * @param {String} value the new value of this element + */ + this.setValue = function(value) { + this.setProperty("value", value, false, true); + }; + + /** + * Returns the current value of this element. + * @return {String} + */ + this.getValue = function() { + return this.value; + }; + + this.increment = function() { + this.change(parseInt(this.oInput.value) + 1); + }; + + this.decrement = function() { + this.change(parseInt(this.oInput.value) - 1); + }; + + + + this.$enable = function() { + this.oInput.disabled = false; + this.$setStyleClass(this.oInput, "", ["inputDisabled"]); + }; + + this.$disable = function() { + this.oInput.disabled = true; + this.$setStyleClass(this.oInput, "inputDisabled"); + }; + + this.$focus = function(e) { + if (!this.$ext || this.focused) //this.disabled || + return; + + + if (apf.hasFocusBug) + apf.sanitizeTextbox(this.oInput); + + + this.focused = true; + this.$setStyleClass(this.oInput, "focus"); + this.$setStyleClass(this.$buttonPlus, "plusFocus"); + this.$setStyleClass(this.$buttonMinus, "minusFocus"); + + if (this.oLeft) + this.$setStyleClass(this.oLeft, "leftFocus"); + }; + + this.$blur = function(e) { + if (!this.$ext && !this.focused) + return; + + this.$setStyleClass(this.oInput, "", ["focus"]); + this.$setStyleClass(this.$buttonPlus, "", ["plusFocus"]); + this.$setStyleClass(this.$buttonMinus, "", ["minusFocus"]); + + if (this.oLeft) + this.$setStyleClass(this.oLeft, "" ["leftFocus"]); + + this.setValue(this.oInput.value); + + this.focused = false; + }; + + /* *********************** + Keyboard Support + ************************/ + + this.addEventListener("keydown", function(e) { + var key = e.keyCode, + + keyAccess = (key < 8 || (key > 9 && key < 37 && key !== 12) + || (key > 40 && key < 46) || (key > 46 && key < 48) + || (key > 57 && key < 96) || (key > 105 && key < 109 && key !== 107) + || (key > 109 && key !== 189)); + + if (keyAccess) + return false; + + switch(key) { + case 38://Arrow up + this.increment(); + break; + case 40://Arrow down + this.decrement(); + break; + } + }, true); + + this.addEventListener("keyup", function(e) { + //this.setValue(this.oInput.value); + }, true); + + + this.increment = function() { + this.change(parseInt(this.oInput.value) + 1); + }; + + this.decrement = function() { + this.change(parseInt(this.oInput.value) - 1); + }; + + /** + * @event click Fires when the user presses a mousebutton while over this element and then let's the mousebutton go. + * @event mouseup Fires when the user lets go of a mousebutton while over this element. + * @event mousedown Fires when the user presses a mousebutton while over this element. + */ + this.$draw = function() { + var _self = this; + + //Build Main Skin + this.$ext = this.$getExternal(null, null, function(oExt) { + oExt.setAttribute("onmousedown", + 'if (!this.host.disabled) \ + this.host.dispatchEvent("mousedown", {htmlEvent : event});'); + oExt.setAttribute("onmouseup", + 'if (!this.host.disabled) \ + this.host.dispatchEvent("mouseup", {htmlEvent : event});'); + oExt.setAttribute("onclick", + 'if (!this.host.disabled) \ + this.host.dispatchEvent("click", {htmlEvent : event});'); + }); + + this.$int = this.$getLayoutNode("main", "container", this.$ext); + this.oInput = this.$getLayoutNode("main", "input", this.$ext); + this.$buttonPlus = this.$getLayoutNode("main", "buttonplus", this.$ext); + this.$buttonMinus = this.$getLayoutNode("main", "buttonminus", this.$ext); + this.oLeft = this.$getLayoutNode("main", "left", this.$ext); + + + apf.sanitizeTextbox(this.oInput); + + + var timer, + doc = (!document.compatMode || document.compatMode == 'CSS1Compat') + ? document.html : document.body, + z = 0; + + /* Setting start value */ + this.oInput.value = this.value; + + this.oInput.onmousedown = function(e) { + if (_self.disabled) + return; + + e = e || window.event; + + clearTimeout(timer); + + var newval, + value = parseInt(this.value) || 0, + step = 0, + cy = e.clientY, + ot = _self.$int.offsetTop, ol = _self.$int.offsetLeft, + ow = _self.$int.offsetWidth, oh = _self.$int.offsetHeight, + func = function() { + clearTimeout(timer); + timer = $setTimeout(func, 10); + if (!step) + return; + + newval = value + step; + if (newval <= _self.max && newval >= _self.min) { + value += step; + value = Math.round(value); + _self.oInput.value = value; + + if (_self.realtime) + _self.change(value); + } + else { + _self.oInput.value = step < 0 + ? _self.min + : _self.max; + } + }; + func(); + + function calcStep(e) { + e = e || window.event; + var x = e.pageX || e.clientX + (doc ? doc.scrollLeft : 0), + y = e.pageY || e.clientY + (doc ? doc.scrollTop : 0), + nrOfPixels = cy - y; + + if ((y > ot && x > ol) && (y < ot + oh && x < ol + ow)) { + step = 0; + return; + } + + step = Math.pow(Math.min(200, Math.abs(nrOfPixels)) / 10, 2) / 10; + if (nrOfPixels < 0) + step = -1 * step; + } + + document.onmousemove = calcStep; + + document.onmouseup = function(e) { + clearTimeout(timer); + + var value = parseInt(_self.oInput.value); + + if (value != _self.value) + _self.change(value); + document.onmousemove = document.onmouseup = null; + }; + }; + + /* Fix for mousedown for IE */ + var buttonDown = false; + this.$buttonPlus.onmousedown = function(e) { + if (_self.disabled) + return; + + e = e || window.event; + buttonDown = true; + + var value = (parseInt(_self.oInput.value) || 0) + 1, + func = function() { + clearTimeout(timer); + timer = $setTimeout(func, 50); + z++; + value += Math.pow(Math.min(200, z) / 10, 2) / 10; + value = Math.round(value); + + _self.oInput.value = value <= _self.max + ? value + : _self.max; + + if (_self.realtime) + _self.change(value <= _self.max ? value : _self.max); + }; + + apf.setStyleClass(this, "plusDown", ["plusHover"]); + + func(); + }; + + this.$buttonMinus.onmousedown = function(e) { + if (_self.disabled) + return; + + e = e || window.event; + buttonDown = true; + + var value = (parseInt(_self.oInput.value) || 0) - 1, + func = function() { + clearTimeout(timer); + timer = $setTimeout(func, 50); + z++; + value -= Math.pow(Math.min(200, z) / 10, 2) / 10; + value = Math.round(value); + + _self.oInput.value = value >= _self.min + ? value + : _self.min; + + if (_self.realtime) + _self.change(value >= _self.min ? value : _self.min); + }; + + apf.setStyleClass(this, "minusDown", ["minusHover"]); + + func(); + }; + + this.$buttonMinus.onmouseout = function(e) { + if (_self.disabled) + return; + + clearTimeout(timer); + z = 0; + + var value = parseInt(_self.oInput.value); + + if (value != _self.value) + _self.change(value); + + apf.setStyleClass(this, "", ["minusHover"]); + + if (!_self.focused) + _self.$blur(e); + }; + + this.$buttonPlus.onmouseout = function(e) { + if (_self.disabled) + return; + + clearTimeout(timer); + z = 0; + + var value = parseInt(_self.oInput.value); + + if (value != _self.value) + _self.change(value); + + apf.setStyleClass(this, "", ["plusHover"]); + + if (!_self.focused) + _self.$blur(e); + }; + + this.$buttonMinus.onmouseover = function(e) { + if (_self.disabled) + return; + + apf.setStyleClass(this, "minusHover"); + }; + + this.$buttonPlus.onmouseover = function(e) { + if (_self.disabled) + return; + + apf.setStyleClass(this, "plusHover"); + }; + + this.$buttonPlus.onmouseup = function(e) { + if (_self.disabled) + return; + + e = e || event; + //e.cancelBubble = true; + apf.cancelBubble(e, this); + + apf.setStyleClass(this, "plusHover", ["plusDown"]); + + clearTimeout(timer); + z = 0; + + var value = parseInt(_self.oInput.value); + + if (!buttonDown) { + value++; + _self.oInput.value = value; + } + else { + buttonDown = false; + } + + if (value != _self.value) + _self.change(value); + }; + + this.$buttonMinus.onmouseup = function(e) { + if (_self.disabled) + return; + + e = e || event; + //e.cancelBubble = true; + apf.cancelBubble(e, this); + + apf.setStyleClass(this, "minusHover", ["minusDown"]); + + clearTimeout(timer); + z = 0; + + var value = parseInt(_self.oInput.value); + + if (!buttonDown) { + value--; + _self.oInput.value = value; + } + else { + buttonDown = false; + } + + + if (value != _self.value) + _self.change(value); + }; + + this.oInput.onselectstart = function(e) { + e = e || event; + e.cancelBubble = true; + }; + + this.oInput.host = this; + }; + + this.$destroy = function() { + this.oInput.onkeypress = + this.oInput.onmousedown = + this.oInput.onkeydown = + this.oInput.onkeyup = + this.oInput.onselectstart = + this.$buttonPlus.onmouseover = + this.$buttonPlus.onmouseout = + this.$buttonPlus.onmousedown = + this.$buttonPlus.onmouseup = + this.$buttonMinus.onmouseover = + this.$buttonMinus.onmouseout = + this.$buttonMinus.onmousedown = + this.$buttonMinus.onmouseup = null; + }; + + + this.$getActiveElements = function() { + // init $activeElements + if (!this.$activeElements) { + this.$activeElements = { + oInput : this.oInput, + $buttonPlus : this.$buttonPlus, + $buttonMinus : this.$buttonMinus + } + } + + return this.$activeElements; + } + + + +}).call(apf.spinner.prototype = new apf.StandardBinding()); + + +apf.aml.setElement("spinner", apf.spinner); + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/elements/splitbutton.js)SIZE(5162)TIME(Tue, 06 Dec 2011 16:22:54 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + + +/** + * Element displaying a skinnable rectangle which can contain other + * aml elements. This element is used by other elements such as the + * toolbar and statusbar element to specify sections within those elements + * which in turn can contain other aml elements. + * Remarks: + * This component is used in the accordion element to create its sections. In + * the statusbar the panel element is an alias of bar. + * + * @constructor + * + * @define bar, panel, menubar + * @attribute {String} icon the url pointing to the icon image. + * @attribute {Boolean} collapsed collapse panel on load, default is false + * Possible values: + * true panel is collapsed + * false panel is not collapsed + * @attribute {String} title describes content in panel + * @allowchild button + * @allowchild {elements}, {anyaml} + * @addnode elements + * + * @author Ruben Daniels (ruben AT ajax DOT org) + * @version %I%, %G% + * @since 0.4 + */ +apf.splitbutton = function(struct, tagName){ + this.$init(tagName || "splitbutton", apf.NODE_VISIBLE, struct); +}; + +(function(){ + this.$focussable = false; + + this.$propHandlers["caption"] = function(value) { + this.$button1.setProperty("caption", value); + } + + this.$propHandlers["icon"] = function(value) { + this.$button1.setProperty("icon", value); + } + + this.$propHandlers["tooltip"] = function(value) { + this.$button1.setProperty("tooltip", value); + } + + this.$propHandlers["hotkey"] = function(value) { + this.$button1.setProperty("hotkey", value); + } + + this.$propHandlers["disabled"] = function(value) { + this.$button1.setProperty("disabled", value); + this.$button2.setProperty("disabled", value); + } + + this.$propHandlers["submenu"] = function(value) { + this.$button2.setProperty("submenu", value); + + var _self = this; + this.$button2.addEventListener("mousedown", function() { + if (!self[value].$splitInited) { + self[value].addEventListener("display", function(){ + var split = this.opener.parentNode; + this.$ext.style.marginLeft = "-" + split.$button1.$ext.offsetWidth + "px"; + }); + self[value].$splitInited = true; + } + + this.removeEventListener("mousedown", arguments.callee); + }); + } + + this.$draw = function(){ + var _self = this; + this.$ext = this.$pHtmlNode.appendChild(document.createElement("div")); + this.$ext.style.overflow = "hidden"; + //this.$ext.style.position = "relative"; + + var skin = this.getAttribute("skin") || this.localName; + + this.$button1 = new apf.button({ + htmlNode: this.$ext, + parentNode: this, + skin: skin, + "class": "main", + onmouseover: function() { + apf.setStyleClass(this.$ext, "primary"); + _self.$button2.$setState("Over", {}); + }, + onmouseout: function() { + apf.setStyleClass(this.$ext, "", ["primary"]); + _self.$button2.$setState("Out", {}); + }, + onclick: function(e) { + _self.dispatchEvent("click"); + } + }); + + this.$button2 = new apf.button({ + htmlNode: this.$ext, + parentNode: this, + skin: skin, + "class": "arrow", + onmouseover: function() { + apf.setStyleClass(this.$ext, "primary"); + _self.$button1.$setState("Over", {}); + }, + onmouseout: function() { + if(!_self.$button2.value) { + apf.setStyleClass(this.$ext, "", ["primary"]); + _self.$button1.$setState("Out", {}); + } + else { + apf.setStyleClass(this.$ext, "primary"); + _self.$button1.$setState("Over", {}); + } + } + }); + }; + + this.$loadAml = function(x){ + + }; + +}).call(apf.splitbutton.prototype = new apf.GuiElement()); + +apf.aml.setElement("splitbutton", apf.splitbutton); + + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/elements/splitter.js)SIZE(16587)TIME(Mon, 19 Dec 2011 11:09:29 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + + +/** + * @constructor + * @private + */ +apf.splitter = function(struct, tagName){ + this.$init(tagName || "splitter", apf.NODE_VISIBLE, struct); +}; + +(function() { + this.$scale = 0; // 0 both, 1 left/top, 2 right/bottom + + this.$focussable = false; // This object can get the focus + this.$splitter = true; + + this.$booleanProperties["realtime"] = true; + + this.$propHandlers["realtime"] = function(value){ + this.$setStyleClass(this.$ext, value && (this.$baseCSSname + "Realtime") || "", + [this.$baseCSSname + "Realtime"]); + } + + this.$propHandlers["scale"] = function(value){ + this.$scale = value == "left" || value == "top" + ? 1 : (value == "right" || "bottom " + ? 2 : 0); + } + + this.$propHandlers["parent"] = function(value){ + this.$parent = typeof value == "object" ? value : self[value]; + } + + this.$propHandlers["type"] = function(value){ + this.$setStyleClass(this.$ext, value, + [value == "horizontal" ? "vertical" : "horizontal"]); + + if (value == "vertical") + this.$setStyleClass(this.$ext, "w-resize", ["n-resize"]); + else + this.$setStyleClass(this.$ext, "n-resize", ["w-resize"]); + + //Optimize this to not recalc for certain cases + if (value == "horizontal") { + this.$info = { + pos : "top", + opos : "left", + size : "width", + osize : "height", + offsetPos : "offsetTop", + offsetSize : "offsetHeight", + oOffsetPos : "offsetLeft", + oOffsetSize : "offsetWidth", + clientPos : "clientY", + d1 : 1, + d2 : 0, + x1 : 0, + x2 : 2 + }; + } + else { + this.$info = { + pos : "left", + opos : "top", + size : "height", + osize : "width", + offsetPos : "offsetLeft", + offsetSize : "offsetWidth", + oOffsetPos : "offsetTop", + oOffsetSize : "offsetHeight", + clientPos : "clientX", + d1 : 0, + d2 : 1, + x1 : 3, + x2 : 1 + } + } + } + + this.addEventListener("DOMNodeInserted", function(e){ + if (e.currentTarget != this) + return; + + /*if (e.$oldParent) { + e.$oldParent.removeEventListener("DOMNodeInserted", this.$siblingChange); + e.$oldParent.removeEventListener("DOMNodeRemoved", this.$siblingChange); + }*/ + + this.init(); + }); + + /*this.$siblingChange = function(e){ + //if (e.currentTarget + + //this.init(); + }*/ + + this.update = function(newPos, finalPass){ + with (this.$info) { + //var pos = Math.ceil(apf.getAbsolutePosition(this.$ext, this.parentNode.$int)[d1] - posPrev[d1]); + var max = this.$previous + ? this.$previous.$ext[offsetSize] + this.$next.$ext[offsetSize] + : (this.parentNode).getWidth(); + var method = finalPass ? "setAttribute" : "setProperty"; + if (apf.hasFlexibleBox) + newPos -= this.$previous ? apf.getAbsolutePosition(this.$previous.$ext, this.parentNode.$int)[d1] : 0; + + //Both flex + if (this.$previous && this.$next && (this.$previous.flex || this.$previous.flex === 0) && (this.$next.flex || this.$next.flex === 0)) { + if (!finalPass && !this.realtime) + newPos -= this.$ext[offsetSize]; + + //var totalFlex = this.$previous.flex + this.$next.flex - (finalPass && !this.realtime ? this.parentNode.padding : 0); + if (!this.$scale || this.$scale == 1) + this.$previous[method]("flex", newPos); + if (!this.$scale || this.$scale == 2) + this.$next[method]("flex", this.$totalFlex - newPos); + } + //Fixed + else { + if (this.$next && !this.$next.flex && (!this.$scale || this.$scale == 2)) + this.$next[method](osize, max - newPos); + if (this.$previous && !this.$previous.flex && (!this.$scale || this.$scale == 1)) + this.$previous[method](osize, newPos); + } + } + + if (apf.hasSingleResizeEvent) + apf.layout.forceResize(this.$ext.parentNode); + }; + + this.$setSiblings = function(){ + this.$previous = this.previousSibling; + while(this.$previous && (this.$previous.nodeType != 1 + || this.$previous.visible === false + || this.$previous.nodeFunc != apf.NODE_VISIBLE)) + this.$previous = this.$previous.previousSibling; + this.$next = this.nextSibling; + while(this.$next && (this.$next.nodeType != 1 + || this.$next.visible === false + || this.$next.nodeFunc != apf.NODE_VISIBLE)) + this.$next = this.$next.nextSibling; + } + + this.init = function(size, refNode, oItem){ + //this.parentNode.addEventListener("DOMNodeInserted", this.$siblingChange); + //this.parentNode.addEventListener("DOMNodeRemoved", this.$siblingChange); + + this.$setSiblings(); + + this.$thickness = null; + if (this.parentNode && this.parentNode.$box) { + this.setProperty("type", this.parentNode.localName == "vbox" + ? "horizontal" + : "vertical"); + this.$thickness = parseInt(this.parentNode.padding); + } + + if (!this.$previous || !this.$next) + return this; + + with (this.$info) { + var diff = apf.getDiff(this.$ext); + if (!this.parentNode.$box) { + var iSize = Math.max( + this.$previous.$ext[offsetSize], this.$next.$ext[offsetSize]); + this.$ext.style[size] = (iSize - diff[d1]) + "px"; + } + + var iThick = this[osize] = this.$thickness + || (this.$next[oOffsetPos] - this.$previous[oOffsetPos] + - this.$previous[oOffsetSize]); + + this.$ext.style[osize] = (iThick - diff[d2]) + "px"; + } + + return this; + }; + + this.$draw = function(){ + //Build Main Skin + this.$ext = this.$getExternal(); + + var _self = this; + this.$ext.onmousedown = function(e){ + if (!e) + e = event; + + apf.dragMode = true; //prevent selection + + _self.$setSiblings(); + + var changedPosition, pHtml = _self.parentNode.$int, diff = 0; + if ("absolute|fixed|relative".indexOf(apf.getStyle(pHtml, "position")) == -1) { + pHtml.style.position = "relative"; + changedPosition = true; + } + + _self.$totalFlex = 0; + with (_self.$info) { + if (_self.$parent) { + if (!_self.$previous) { + var posNext = apf.getAbsolutePosition(_self.$next.$ext, _self.parentNode.$int); + var wd = _self.$parent.getWidth(); + + if (_self.$scale == 2) { + var max = posNext[d1] + _self.$next.$ext[offsetSize] - this[offsetSize]; + diff = (_self.parentNode.$int[offsetSize] - max); + var min = max - wd - diff; + } + } + else if (!_self.$next) { + //@todo + } + } + else { + if (_self.$previous) { + var posPrev = apf.getAbsolutePosition(_self.$previous.$ext, _self.parentNode.$int); + var min = _self.$scale ? 0 : posPrev[d1] || 0; + } + if (_self.$next) { + var posNext = apf.getAbsolutePosition(_self.$next.$ext, _self.parentNode.$int); + var max = posNext[d1] + _self.$next.$ext[offsetSize] - this[offsetSize]; + } + } + + //Set flex to pixel sizes + if (_self.$previous && _self.$next) { + if ((_self.$previous.flex || _self.$previous.flex === 0) + && (_self.$next.flex || _self.$next.flex === 0)) { + var set = [], nodes = _self.parentNode.childNodes, padding = 0; + for (var node, i = 0, l = nodes.length; i < l; i++) { + if ((node = nodes[i]).visible === false + || node.nodeFunc != apf.NODE_VISIBLE || node.$splitter) + continue; + + if (node.flex) + set.push(node, node.$ext[offsetSize] + + (apf.hasFlexibleBox && !_self.realtime && node == _self.$previous + ? 2 * _self.parentNode.padding : 0)); + } + for (var i = 0, l = set.length; i < l; i+=2) { + set[i].setAttribute("flex", set[i+1]); + } + } + + _self.$totalFlex += _self.$next.flex + _self.$previous.flex; + } + + var startPos, startOffset; + if (apf.hasFlexibleBox) { + var coords = apf.getAbsolutePosition(this); + startPos = e[clientPos] - coords[d1]; + + if (!_self.realtime) { + if (_self.$previous.flex && !_self.$next.flex) { + var mBox = apf.getBox(_self.$next.margin); + mBox[x1] = _self.parentNode.padding; + _self.$next.$ext.style.margin = mBox.join("px ") + "px"; + } + else { + var mBox = apf.getBox(_self.$previous.margin); + mBox[x2] = _self.parentNode.padding; + _self.$previous.$ext.style.margin = mBox.join("px ") + "px"; + } + + var diff = apf.getDiff(this); + this.style.left = coords[0] + "px"; + this.style.top = coords[1] + "px"; //(apf.getHtmlTop(this) - Math.ceil(this.offsetHeight/2)) + this.style.width = (this.offsetWidth - diff[0]) + "px"; + this.style.height = (this.offsetHeight - diff[1]) + "px"; + this.style.position = "absolute"; + } + } + else { + var coords = apf.getAbsolutePosition(this.offsetParent); + startOffset = apf.getAbsolutePosition(_self.$previous.$ext)[d1]; + startPos = e[clientPos] - coords[d1]; + + if (!_self.realtime) { + this.style.left = "0px"; + this.style.top = "0px"; + this.style.position = "relative"; + } + min = -1000; //@todo + } + } + + //e.returnValue = false; + //e.cancelBubble = true; + //apf.stopEvent(e); + + + apf.plane.show(this); + + + _self.$setStyleClass(this, _self.$baseCSSname + "Moving"); + + _self.$setStyleClass(document.body, + _self.type == "vertical" ? "w-resize" : "n-resize", + [_self.type == "vertical" ? "n-resize" : "w-resize"]); + + _self.dispatchEvent("dragstart"); + + //@todo convert to proper way + document.onmouseup = function(e){ + if(!e) e = event; + + with (_self.$info) { + var newPos; + if (e[clientPos] >= 0) { + var coords = apf.getAbsolutePosition(_self.$ext.offsetParent); + newPos = (Math.min(max, Math.max(min, (e[clientPos] - coords[d1]) - + (apf.hasFlexibleBox ? startPos : startOffset)))) + diff; + } + } + + _self.$setStyleClass(_self.$ext, "", [_self.$baseCSSname + "Moving"]); + _self.$setStyleClass(document.body, "", ["n-resize", "w-resize"]); + + if (changedPosition) + pHtml.style.position = ""; + + if (apf.hasFlexibleBox && !_self.realtime) + (_self.$previous.flex && !_self.$next.flex + ? _self.$next : _self.$previous).$ext.style.margin + = apf.getBox(_self.$previous.margin).join("px ") + "px"; + + if (newPos) + _self.update(newPos, true); + + + apf.plane.hide(); + + + if (!_self.realtime) { + _self.$ext.style.left = ""; + _self.$ext.style.top = ""; + _self.$ext.style[_self.$info.size] = ""; + _self.$ext.style.position = ""; + } + + _self.dispatchEvent("dragdrop"); + + document.onmouseup = + document.onmousemove = null; + + apf.dragMode = false; //return to default selection policy + }; + + //@todo convert to proper way + document.onmousemove = function(e){ + if(!e) e = event; + + with (_self.$info) { + var newPos; + if (e[clientPos] >= 0) { + var coords = apf.getAbsolutePosition(_self.$ext.offsetParent); + newPos = (Math.min(max, Math.max(min, (e[clientPos] - coords[d1]) - + (apf.hasFlexibleBox || !_self.realtime ? startPos : startOffset)))) + diff; + + if (_self.realtime) + _self.update(newPos); + else { + _self.$ext.style[pos] = newPos + "px"; + } + } + } + + apf.stopEvent(e); + //e.returnValue = false; + //e.cancelBubble = true; + + _self.dispatchEvent("dragmove"); + }; + } + + apf.queue.add("splitter" + this.$uniqueId, function(){ + _self.init(); + }); + }; + + this.$loadAml = function(x){ + if (this.realtime !== false) // && (!apf.isIE || apf.isIE > 8)) + this.$propHandlers.realtime.call(this, this.realtime = true); + }; +}).call(apf.splitter.prototype = new apf.Presentation()); + +apf.aml.setElement("splitter", apf.splitter); + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/elements/state-group.js)SIZE(3131)TIME(Fri, 14 Oct 2011 08:01:44 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + +/** + * Element that groups state elements together and + * provides a way to set a default state. + * Example: + * + * + * + * + * + * + * + * + * @addnode elements + * @see element.state + * + * @author Ruben Daniels (ruben AT ajax DOT org) + * @version %I%, %G% + * @since 0.4 + */ +apf.stateGroup = function(){ + this.$init("state-group", apf.NODE_HIDDEN); +}; +apf.aml.setElement("state-group", apf.stateGroup); + +(function(){ + this.$handlePropSet = function(prop, value, force){ + if (prop == "id") + return; + + var node, nodes = this.childNodes; + for (var i = 0, l = nodes.length; i < l; i++){ + node = nodes[i]; + + if (node.nodeType != 1 || node.localName != "state") + continue; + + if (!node[prop] || node.$inheritProperties[prop] == 2) { + node.$inheritProperties[prop] = 2; + node.setProperty(prop, value); + } + } + }; + + //@todo this should use text node insertion + this.addEventListener("DOMNodeInsertedIntoDocument", function(e){ + if (!this.id) + this.id = "stategroup" + this.$uniqueId; + + //apf.StateServer.addGroup(this.id, null, this.parentNode); //@todo rearch this + + var nodes = this.childNodes; + for (var i = 0, l = nodes.length; i < l; i++){ + nodes[i].setProperty("group", this.id); + } + }); +}).call(apf.stateGroup.prototype = new apf.AmlElement()); + + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/elements/state.js)SIZE(10893)TIME(Wed, 30 Nov 2011 17:29:20 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + +/** + * @private + */ +apf.StateServer = { + states: {}, + groups: {}, + locs : {}, + + removeGroup: function(name, elState){ + this.groups[name].remove(elState); + if (!this.groups[name].length) { + if (self[name]) { + self[name].destroy(); + self[name] = null; + } + + delete this.groups[name]; + } + }, + + addGroup: function(name, elState, pNode){ + if (!this.groups[name]) { + this.groups[name] = []; + + var pState = new apf.state({ + id : name + }); + pState.parentNode = pNode; + //pState.implement(apf.AmlNode); + //pState.name = name; + pState.toggle = function(){ + for (var next = 0, i = 0; i < apf.StateServer.groups[name].length; i++) { + if (apf.StateServer.groups[name][i].active) { + next = i + 1; + break; + } + } + + apf.StateServer.groups[name][ + (next == apf.StateServer.groups[name].length) ? 0 : next + ].activate(); + } + + this.groups[name].pState = self[name] = pState; + } + + if (elState) + this.groups[name].push(elState); + + return this.groups[name].pState; + }, + + removeState: function(elState){ + delete this.states[elState.name]; + }, + + addState: function(elState){ + this.states[elState.name] = elState; + } +} + +/** + * Element that specifies a certain state of (a part of) the application. With + * state we mean a collection of properties on objects that have a certain + * value at one time. This element allows you to specify which properties on + * which elements should be set when a state is activated. This element can + * belong to a state-group containing multiple elements with a default state. + * Example: + * This example shows a log in window and four state elements in a state-group. + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * Username + * + * + * Password + * + * + * + * Log in + * + * Log out + * + * Example: + * This example shows a label using property binding to get it's caption + * based on the current state. + * + * + * + * + * + * Become admin + * + * + * @event change Fires when the active property of this element changes. + * + * @constructor + * @define state + * @addnode global + * + * @author Ruben Daniels (ruben AT ajax DOT org) + * @version %I%, %G% + * @since 0.9 + */ +apf.state = function(struct, tagName){ + this.$init(tagName || "state", apf.NODE_HIDDEN, struct); + + this.$signalElements = []; + this.$groupAdded = {}; + this.$locationAdded = ''; +}; + +(function(){ + /**** Properties and Attributes ****/ + + this.$supportedProperties.push("active"); + this.$booleanProperties["active"] = true; + + /** + * @attribute {Boolean} active whether this state is the active state + */ + this.$propHandlers["active"] = function(value){ + //Activate State + if (apf.isTrue(value)) { + if (this.group) { + var nodes = apf.StateServer.groups[this.group]; + if (!nodes) { + apf.StateServer.addGroup(this.group, this); + nodes = apf.StateServer.groups[this.group]; + } + + for (var i = 0; i < nodes.length; i++) { + if (nodes[i] != this && nodes[i].active !== false) + nodes[i].deactivate(); + } + } + + var q = this.$signalElements; + for (var i = 0; i < q.length; i++) { + if (!self[q[i][0]] || !self[q[i][0]].setProperty) { + + + continue; + } + + self[q[i][0]].setProperty(q[i][1], this[q[i].join(".")]); + } + + if (this.group) { + var attr = this.attributes; + for (var i = 0; i < attr.length; i++) { + if (attr[i].nodeName.match(/^on|^(?:group|id)$|^.*\..*$/)) + continue; + self[this.group].setProperty(attr[i].nodeName, + attr[i].nodeValue); + } + apf.StateServer.groups[this.group].pState.dispatchEvent("change"); + } + + this.dispatchEvent("activate"); + + + } + + //Deactivate State + else { + this.setProperty("active", false); + this.dispatchEvent("deactivate"); + + + } + }; + + + /**** Public methods ****/ + + + + /** + * Sets the value of this element. This should be one of the values + * specified in the values attribute. + * @param {String} value the new value of this element + */ + this.setValue = function(value){ + this.active = 9999; + this.setProperty("active", value, false, true); + }; + + /** + * Actives this state, setting all the properties on the elements that + * were specified. + */ + this.activate = function(){ + this.active = 9999; + this.setProperty("active", true, false, true); + }; + + /** + * Deactivates the state of this element. This is mostly a way to let all + * elements that have property bound to this state know it is no longer + * active. + */ + this.deactivate = function(){ + this.setProperty("active", false, false, true); + }; + + + + /**** Init ****/ + + this.$propHandlers["group"] = function(value){ + if (value) { + apf.StateServer.addGroup(value, this); + this.$groupAdded = {'value' : value, elState : this}; + } + else { + apf.StateServer.removeGroup(this.$groupAdded.value, this.$groupAdded.elState); + this.$groupAdded = {}; + } + } + + this.$propHandlers["location"] = function(value){ + if (value) { + apf.StateServer.locs[value] = this; + this.$locationAdded = value; + } + else { + delete apf.StateServer.locs[this.$locationAdded]; + this.$locationAdded = ''; + } + } + + this.addEventListener("DOMNodeInsertedIntoDocument", function(e){ + apf.StateServer.addState(this); + + //Properties initialization + var attr = this.attributes; + for (var s, i = 0; i < attr.length; i++) { + s = attr[i].nodeName.split("."); + if (s.length == 2) + this.$signalElements.push(s); + } + }); + + this.addEventListener("DOMNodeRemovedFromDocument", function(){ + this.$signalElements = null; + apf.StateServer.removeState(this); + if (this.group) + apf.StateServer.removeGroup(this.group, this); + }); +}).call(apf.state.prototype = new apf.AmlElement()); + +apf.aml.setElement("state", apf.state); + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/elements/statusbar.js)SIZE(3824)TIME(Fri, 14 Oct 2011 08:01:44 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + + +/** + * Element displaying a bar consisting of bars containing other text, icons + * and more aml. This element is usually placed in the bottom of the screen to + * display context sensitive and other information about the state of the + * application. + * Example: + * + * + * Ajax.org + * Some status information + * + * + * + * + * + * + * @constructor + * @define statusbar + * @allowchild bar + * @allowchild progressbar + * @addnode elements + * + * @author Ruben Daniels (ruben AT ajax DOT org) + * @version %I%, %G% + * @since 0.9 + */ +apf.statusbar = function(struct, tagName){ + this.$init(tagName || "statusbar", apf.NODE_VISIBLE, struct); +}; + +(function(){ + this.$focussable = false; + + /**** DOM Hooks ****/ + var insertChild; + + this.addEventListener("AMLRemoveChild", function(amlNode, doOnlyAdmin){ + if (doOnlyAdmin) + return; + + }); + + this.addEventListener("AMLInsert",insertChild = function (amlNode, beforeNode, withinParent){ + if (amlNode.tagName != "bar") + return; + + amlNode.$propHandlers["caption"] = function(value){ + apf.setNodeValue( + this.$getLayoutNode("bar", "caption", this.$ext), value); + } + amlNode.$propHandlers["icon"] = function(value){ + var oIcon = this.$getLayoutNode("bar", "icon", this.$ext); + if (!oIcon) return; + + if (value) + this.$setStyleClass(this.$ext, this.$baseCSSname + "Icon"); + else + this.$setStyleClass(this.$ext, "", [this.$baseCSSname + "Icon"]); + + if (oIcon.tagName == "img") + oIcon.setAttribute("src", value ? this.iconPath + value : ""); + else { + oIcon.style.backgroundImage = value + ? "url(" + this.iconPath + value + ")" + : ""; + } + } + }); + + + /**** Init ****/ + + this.$draw = function(){ + //Build Main Skin + this.$ext = this.$getExternal(); + this.$int = this.$getLayoutNode("main", "container", this.$ext); + }; + + this.$loadAml = function(x){ + var nodes = this.childNodes; + for (var i = nodes.length - 1; i >= 0; i--) { + if (nodes[i].localName == "section") { + nodes[i].addEventListener("DOMNodeInsertedIntoDocument", function(){ + this.$setStyleClass(this.$ext, this.$baseCSSname + "Last"); + }); + break; + } + } + }; +}).call(apf.statusbar.prototype = new apf.Presentation()); + +apf.aml.setElement("statusbar", apf.statusbar); + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/elements/style.js)SIZE(1888)TIME(Fri, 14 Oct 2011 08:01:44 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + + +/** + * @todo description + * + * @author Ruben Daniels (ruben AT ajax DOT org) + * @version %I%, %G% + * @since 0.4 + */ +apf.style = function(struct, tagName){ + this.$init(tagName || "style", apf.NODE_HIDDEN, struct); +}; + +(function(){ + this.$focussable = false; + + this.$propHandlers["src"] = function(value){ + apf.getData(value, { + callback : function(data, state){ + if (state == apf.SUCCESS) { + apf.importCssString(data); + } + } + }); + } + + this.addEventListener("DOMNodeInsertedIntoDocument", function(e){ + if (this.type != "text/chartcss" && this.firstChild) + apf.importCssString(this.firstChild.nodeValue); + }); +}).call(apf.style.prototype = new apf.AmlElement()); + +apf.aml.setElement("style", apf.style); +apf.xhtml.setElement("style", apf.style); + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/elements/submitform.js)SIZE(30092)TIME(Fri, 14 Oct 2011 08:01:44 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/elements/tab.js)SIZE(2990)TIME(Fri, 14 Oct 2011 08:01:44 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + + +/** + * Element displaying a page and several buttons allowing a + * user to switch between the pages. Each page can contain + * arbitrary aml. Each page can render it's content during + * startup of the application or when the page is activated. + * Example: + * + * + * + * Example + * Example + * + * + * Test checkbox + * Test checkbox + * Test checkbox + * + * + * This ok? + * This better? + * + * + * + * + * @constructor + * @define tab, pages, switch + * @allowchild page + * @addnode elements + * + * @author Ruben Daniels (ruben AT ajax DOT org) + * @version %I%, %G% + * @since 0.1 + * + * @inherits apf.BaseTab + */ + +apf["switch"] = function(struct, tagName){ + this.$hasButtons = false; + this.$init(tagName || "switch", apf.NODE_VISIBLE, struct); +}; + +apf.pages = function(struct, tagName){ + this.$hasButtons = false; + this.$init(tagName || "pages", apf.NODE_VISIBLE, struct); + + this.$focussable = false; +}; + +apf.tab = function(struct, tagName){ + this.$hasButtons = true; + this.$init(tagName || "tab", apf.NODE_VISIBLE, struct); +}; + +(function(){ + this.$focussable = apf.KEYBOARD; // This object can get the focus from the keyboard + + /**** Init ****/ + + this.$draw = function(bSkinChange){ + //Build Main Skin + this.$ext = this.$getExternal(); + this.$loadChildren(); + }; +}).call(apf.tab.prototype = new apf.BaseTab()); + +apf["switch"].prototype = +apf.pages.prototype = apf.tab.prototype; + +apf.aml.setElement("switch", apf["switch"]); +apf.aml.setElement("pages", apf.pages); +apf.aml.setElement("tab", apf.tab); + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/elements/table.js)SIZE(17204)TIME(Wed, 30 Nov 2011 17:29:20 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + +/** + * Any child element of this element is placed in a table. The size of the + * columns and rows of the table can be set by attributes. Child elements can + * span multiple columns. Using '*' as a size indicator will use the remaining + * size for that column or row, when the table's size is set. + * Example: + * This example shows a window with a table and two buttons that change the + * orientation of the table runtime. The textarea and it's label have a span set + * to '*'. This means they will span the entire width of all columns, no matter + * how many columns there are. + * + * + * + * Name + * + * Address + * + * Country + * + * + * Message + * + * + * + * + * + * + * + * + * Remarks: + * This is one of three positioning methods. + * See {@link baseclass.alignment} + * See {@link baseclass.anchoring} + * + * @define table + * @allowchild {elements}, {anyaml} + * @addnode elements + * @constructor + * + * @author Ruben Daniels (ruben AT ajax DOT org) + * @version %I%, %G% + * @since 1.0 + */ +apf.table = function(struct, tagName){ + this.$init(tagName || "table", apf.NODE_VISIBLE, struct); +}; + +(function(){ + /**** Properties and Attributes ****/ + + this.$focussable = false; + this.$useLateDom = true; + this.$layout = true; + + this.columns = null;//"150,200"; + this.padding = 2; + this.$edge = [5, 5, 5, 5]; + this.cellheight = 19; + + /** + * @attribute {String} columns a comma seperated list of column sizes. A column size can be specified in a number (size in pixels) or using a number and a % sign to indicate a percentage. A '*' indicates the column spans the rest space. There can be only one '*' in the column string. + * Example: + * + * + * + * @attribute {String} padding the space between each element. Defaults to 2. + * @attribute {String} edge the space between the container and the elements, space seperated in pixels for each side. Similar to css in the sequence top right bottom left. Defaults to "5 5 5 5". + * Example: + * + * + * + */ + this.$supportedProperties.push("columns", "padding", "edge", + "cellheight", "span"); + + this.$propHandlers["columns"] = function(value){ + if (!value.match(/^((?:\d+\%?|\*)\s*(?:,\s*|\s*$))+$/)) { + + return; + } + + var col, colsize = this.$columns = value.splitSafe(","); + + var total = 0, cols = this.$table.getElementsByTagName("col"); + if (cols.length) { + for (var sz, i = 0, l = Math.min(cols.length, colsize.length); i < l; i++) { + cols[i].style.width = (sz = colsize[i]).indexOf("%") > -1 ? sz : sz + "px"; + total += parseInt(sz); + } + } + + var start = cols.length - colsize.length; + if (start > 0) { + for (var i = cols.length - start; i < cols.length; i++) { + cols[i].parentNode.removeChild(cols[i]); + } + } + else if (start < 0) { + for (var i = colsize.length + start; i < colsize.length; i++) { + col = this.$table.appendChild(document.createElement("col")); + col.style.width = (sz = colsize[i]).indexOf("%") > -1 ? sz : sz + "px"; + col.setAttribute("valign", "top"); + total += parseInt(sz); + } + } + + this.$table.style.width = String(value).indexOf("%") > -1 + ? "auto" + : (total + ((colsize.length - 1) * this.padding) + + this.$edge[0] + this.$edge[2]) + "px"; + + var cells = this.$tbody.firstChild.getElementsByTagName("td"); + for (var i = cells.length - 1; i >= 0; i--) + cells[i].parentNode.removeChild(cells[i]); + + for (var sz, c, i = 0; i < colsize.length; i++) { + c = this.$tbody.firstChild.appendChild(document.createElement("td")); + c.style.width = (sz = colsize[i]).indexOf("%") > -1 ? sz : sz + "px"; + + /*if (colsize[i].indexOf("%") > -1) + c.appendChild(document.createElement("div")).style.width = "50px";*/ + } + + if (start && this.$amlLoaded) + visibleHandler({sync: true, parentNode: this}); + + this.$resize(); + } + + this.$propHandlers["padding"] = function(value){ + if (!this.$columns) return; + var cells = this.$table.getElementsByTagName("td"); + var lastCol, lastRow, cell, lRow = this.$tbody.lastChild; + for (var i = this.$columns.length, l = cells.length; i < l; i++) { + lastCol = (cell = cells[i]).parentNode.lastChild == cell; + lastRow = cell.parentNode == lRow; + cell.style.padding = "0px " + (lastCol ? 0 : value) + "px " + (lastRow ? 0 : value) + "px 0px"; + } + this.$resize(); + } + + this.$propHandlers["edge"] = function(value){ + this.$table.style.padding = (this.$edge = apf.getBox(value)).join("px ") + "px"; + this.$resize(); + } + + function visibleHandler(e){ + var table = e.parentNode || this.parentNode; + if (e.sync || e.value && !this.$altExt || !e.value && this.$altExt) { + var nodes = table.childNodes; + + var cells = apf.getArrayFromNodelist(table.$tbody.getElementsByTagName("td")); + var rows = table.$tbody.getElementsByTagName("tr"); + var empty = [], row = 1, cs, rs, collen = table.$columns.length; + var z = table.$columns.length, lastCol; + for (var node, td, last, l = nodes.length, i = 0; i < l; i++) { + if ((node = nodes[i]).visible === false) + continue; + + td = node.$altExt = last = cells[z++]; + if (!td) break; + //td = node.$altExt = last = document.createElement("td"); + + if (!rows[row]) + table.$tbody.appendChild(document.createElement("tr")); + + rows[row].appendChild(td); + td.appendChild(node.$ext); + td.setAttribute("colspan", cs = Math.min(collen - (empty[0] || 0), parseInt(node.colspan || node.span || 1))); + td.setAttribute("rowspan", rs = parseInt(node.rowspan || 1)); + + //@todo this is wrong it should be cs * rs + if (!empty[0]) + empty[0] = 0; + empty[0] += cs; + + if (rs > 1) { + for (var k = 1; k < rs; k++) { + if (!empty[k]) + empty[k] = 0; + empty[k] += cs; + } + } + + if (empty[0] >= collen) { + lastCol = true; + empty.shift(); + row++; + } + else lastCol = false; + + td.style.padding = "0px " + (lastCol ? 0 : table.padding) + + "px " + (i == l - 1 ? 0 : table.padding) + "px 0px"; + } + + //Fix padding of last row + var lastCells = rows[rows.length - 1].getElementsByTagName("td"); + for (i = 0, il = lastCells.length; i < il; i++) { + lastCells[i].style.padding = "0 " + + (i == il - 1 ? 0 : table.padding) + "px 0 0" + } + + for (;z < cells.length; z++) + cells[z].parentNode.removeChild(cells[z]); + + if (e.sync) return; + + if (e.value) + table.$addTd(nodes[l - 1]); //what if it's not visible + else { + //last.parentNode.removeChild(last); + this.$altExt = null; + } + } + } + + this.$addTd = function(amlNode){ + var cells = this.$table.getElementsByTagName("td"); + var total = 0, collen = this.$columns.length; + for (var cell, i = 0; i < cells.length; i++) { + total += Math.min(collen, + (parseInt((cell = cells[i]).getAttribute("colspan") || 1) + * parseInt(cell.getAttribute("rowspan") || 1))); + } + + if (total % collen == 0) { //New Row + var row = this.$tbody.appendChild(document.createElement("tr")); + } + else + row = cells[cells.length - 1].parentNode; + + //Add a new cell in the last row + var cel = row.appendChild(document.createElement("td")); + cel.style.position = "relative"; + + if (amlNode.colspan || amlNode.span) + cel.setAttribute("colspan", amlNode.colspan || amlNode.span); + if (amlNode.rowspan) + cel.setAttribute("rowspan", amlNode.rowspan); + + cel.appendChild(amlNode.$ext); + + amlNode.$altExt = cel; + } + + var propHandlers = { + "width" : function(value){ + this.$ext.style.width = "";/*value + ? Math.max(0, value - apf.getWidthDiff(this.$ext)) + "px" + : "";*/ + }, + + "height" : function(value){ + this.$ext.style.height = value + ? Math.max(0, value - apf.getHeightDiff(this.$ext)) + "px" + : ""; + this.parentNode.$resize(); + }, + + "margin" : function(value){ + this.$ext.style.margin = apf.getBox(value).join("px ") + "px"; + this.parentNode.$resize(); + }, + + "colspan" : function(value){ + if (!value) + this.$altExt.removeAttribute("colspan"); + else + this.$altExt.setAttribute("colspan", value); + + visibleHandler.call(this, {sync: true}); + this.parentNode.$resize(); + }, + + "rowspan" : function(value){ + if (!value) + this.$altExt.removeAttribute("rowspan"); + else + this.$altExt.setAttribute("rowspan", value); + + visibleHandler.call(this, {sync: true}); + this.parentNode.$resize(); + }, + + "valign" : function(value){ + this.$altExt.valign = value; + }, + + "align" : function(value){ + if ("left|right".indexOf(value) == -1) + return; + + this.$altExt.align = value; + } + } + propHandlers.span = propHandlers.colspan; + + //@todo move this to enableTable, disableTable + this.register = function(amlNode){ + if (amlNode.$altExt) //@todo hack, need to rearch layouting + return; + + amlNode.$propHandlers["left"] = + amlNode.$propHandlers["top"] = + amlNode.$propHandlers["right"] = + amlNode.$propHandlers["bottom"] = apf.K; + + for (var prop in propHandlers) { + amlNode.$propHandlers[prop] = propHandlers[prop]; + } + + amlNode.addEventListener("prop.visible", visibleHandler); + + this.$addTd(amlNode); + + this.$noResize = true; + + if (amlNode.margin) + propHandlers.margin.call(amlNode, amlNode.margin); + + //Why was this commented out? + if (amlNode.$ext.tagName == "INPUT") { + //amlNode.$ext.style.width = "100%"; + } + else + amlNode.$ext.style.width = "auto"; + + if (this.lastChild == amlNode) + this.$propHandlers["padding"].call(this, this.padding); + + delete this.$noResize; + } + + this.unregister = function(amlNode){ + amlNode.$propHandlers["left"] = + amlNode.$propHandlers["top"] = + amlNode.$propHandlers["right"] = + amlNode.$propHandlers["bottom"] = null; + + for (var prop in propHandlers) { + delete amlNode.$propHandlers[prop]; + } + + amlNode.removeEventListener("prop.visible", visibleHandler); + visibleHandler.call(amlNode, {value: false}); //maybe parent is already reset here? + + if (amlNode.margin) + amlNode.$ext.style.margin = ""; + + if (amlNode.width) + amlNode.$ext.style.width = ""; + } + /* + this.addEventListener("DOMNodeInsertedIntoDocument", function(e){ + this.register(this.parentNode); + }); + */ + + /**** DOM Hooks ****/ + + this.addEventListener("DOMNodeRemoved", function(e){ + if (e.$doOnlyAdmin || e.currentTarget == this) + return; + + if (e.relatedNode == this){ + this.unregister(e.currentTarget); + //e.currentTarget.$setLayout(); + } + }); + + this.addEventListener("DOMNodeInserted", function(e){ + if (e.currentTarget == this || e.currentTarget.nodeType != 1) + return; + + if (e.relatedNode == this) { + if (e.$isMoveWithinParent) { + visibleHandler.call(e.currentTarget, {sync: true}); + } + else { + e.currentTarget.$setLayout("table"); + if (e.currentTarget.nextSibling) + visibleHandler.call(e.currentTarget, {sync: true}); + } + } + }); + + this.$draw = function(){ + this.$ext = apf.insertHtmlNode(null, this.$pHtmlNode, null, + "
"); + this.$table = this.$ext.firstChild; + this.$tbody = this.$table.firstChild; + this.$ext.className = "table " + (this.getAttribute("class") || ""); + //this.$ext.style.overflow = "hidden"; + this.$int = this.$ext; + this.$ext.host = this; + + if (this.getAttribute("class")) + apf.setStyleClass(this.$ext, this.getAttribute("class")); + + this.addEventListener("resize", this.$resize); + this.$originalMin = [this.minwidth || 0, this.minheight || 0]; + }; + + //@todo implement percentage by using fixed and add functionality here + this.$resize = function(){ + if (!this.$amlLoaded || this.$noResize) + return; + + if (this.$table.offsetWidth >= this.$ext.offsetWidth) + this.$ext.style.minWidth = (this.minwidth = Math.max(0, this.$table.offsetWidth + - apf.getWidthDiff(this.$ext))) + "px"; + else { + this.$ext.style.minWidth = ""; + this.minwidth = this.$originalMin[0]; + } + + if (this.$table.offsetHeight >= this.$ext.offsetHeight) + this.$ext.style.minHeight = (this.minheight = Math.max(0, this.$table.offsetHeight + - apf.getHeightDiff(this.$ext))) + "px"; + else { + this.$ext.style.minHeight = ""; + this.minheight = this.$originalMin[1]; + } + } + + this.$loadAml = function(x){ + this.$amlLoaded = false; //@todo hack + + if (!this.$columns) + this.$propHandlers.columns.call(this, this.columns = "150, 200"); + this.$amlLoaded = true; //@todo hack + }; +}).call(apf.table.prototype = new apf.GuiElement()); + +apf.aml.setElement("table", apf.table); + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/elements/teleport.js)SIZE(1019)TIME(Fri, 14 Oct 2011 08:01:44 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + + +apf.aml.setElement("teleport", apf.AmlElement); + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/elements/template.js)SIZE(2498)TIME(Fri, 14 Oct 2011 08:01:44 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/elements/text.js)SIZE(12619)TIME(Wed, 19 Oct 2011 09:20:28 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + + +/** + * Element displaying a rectangle containing arbitrary (X)HTML. + * This element can be databound and use databounding rules to + * convert data into (X)HTML using for instance XSLT or JSLT. + * + * @constructor + * @define text + * @addnode elements + * + * @inherits apf.Cache + * + * @author Ruben Daniels (ruben AT ajax DOT org) + * @version %I%, %G% + * @since 0.1 + * @todo Please refactor this object + */ +apf.text = function(struct, tagName){ + this.$init(tagName || "text", apf.NODE_VISIBLE, struct); + + this.$nodes = []; +}; + +(function(){ + this.implement( + + apf.Cache, + + apf.ChildValue + ); + + this.$focussable = true; // This object can't get the focus + this.focussable = false; + this.textselect = true; + this.$hasStateMessages = true; + + this.$textTimer = this.$lastMsg = this.$lastClass = this.$changedHeight = null; + + /**** Properties and Attributes ****/ + + /** + * @attribute {Boolean} scrolldown whether this elements viewport is always + * scrolled down. This is especially useful + * when this element is used to displayed + * streaming content such as a chat conversation. + * @attribute {Boolean} secure whether the content loaded in this element + * should be filtered in order for it to not + * be able to execute javascript. This is + * especially useful when the content does + * not come from a trusted source, like a + * web service or xmpp feed. + */ + this.$booleanProperties["scrolldown"] = true; + this.$booleanProperties["secure"] = true; + this.$booleanProperties["textselect"] = true; + this.$supportedProperties.push("behavior", "scrolldown", "secure", "value"); + + this.$isTextInput = function(){ + return this.textselect; + } + + this.$propHandlers["scrolldown"] = function(value){ + var _self = this; + + if (value) { + //this.addEventListener("resize", this.$resize); + this.$scrolldown = true; + apf.addListener(this.$scrollArea, "scroll", this.$scrollFunc = function(){ + _self.$scrolldown = this.scrollTop >= this.scrollHeight + - this.offsetHeight + apf.getVerBorders(this); + }); + this.addEventListener("scroll", this.$scroll); + this.addEventListener("afterload", this.$scroll); + this.addEventListener("resize", function(){ + if (_self.$scrollArea && _self.$scrolldown && _self.scrolldown) + _self.$scrollArea.scrollTop = _self.$scrollArea.scrollHeight; + }); + clearInterval(this.$textTimer); + this.$textTimer = setInterval(function(){ + if (_self.$scrollArea && _self.$scrolldown && _self.scrolldown) + _self.$scrollArea.scrollTop = _self.$scrollArea.scrollHeight; + }, 200); + } + else { + //this.removeEventListener("resize", this.$resize); + + this.removeEventListener("scroll", this.$scroll); + this.removeEventListener("afterload", this.$scroll); + clearInterval(this.$textTimer); + if (this.$scrollArea) + apf.removeListener(this.$scrollArea, "scoll", this.$scrollFunc); + } + } + + this.$scroll = function(e){ + var html = this.$scrollArea; + + if (e.name == "afterload") { + this.$scrolldown = true; + html.scrollTop = html.scrollHeight; + return; + } + + this.$scrolldown = html.scrollTop >= html.scrollHeight + - html.offsetHeight + apf.getVerBorders(html); + }; + + /*this.$resize = function(){ + if (this.scrolldown && this.$scrolldown) + this.$scrollArea.scrollTop = this.$scrollArea.scrollHeight; + }*/ + + /** + * @attribute {String} value the contents of this element. This can be text or html or xhtml. + */ + this.$propHandlers["value"] = function(value, prop, force, forceAdd){ + if (this.each) + return; + + if (typeof value != "string") { + if (value.nodeType) + value = value.nodeType > 1 && value.nodeType < 5 + ? value.nodeValue + : value.firstChild && value.firstChild.nodeValue || ""; + else + value = value ? value.toString() : ""; + } + + if (this.secure) { + value = value.replace(//g, "") + .replace(//g, "") + .replace(new RegExp("ondblclick|onclick|onmouseover|onmouseout" + + "|onmousedown|onmousemove|onkeypress|onkeydown|onkeyup|onchange" + + "|onpropertychange", "g"), "ona"); + } + + value = value.replace(/\<\?xml version="1\.0" encoding="UTF-16"\?\>/, ""); + + if (forceAdd) { + apf.insertHtmlNodes(null, this.$container, null, value); + if (!this.value) this.value = ""; + this.value += value; + } + else + this.$container.innerHTML = value;//.replace(//ig, "") + + //Iframe bug fix for IE (leaves screen white); + if (apf.cannotSizeIframe && this.oIframe) + this.oIframe.style.width = this.oIframe.offsetWidth + "px"; + + if (this.scrolldown && this.$scrolldown) + this.$scrollArea.scrollTop = this.$scrollArea.scrollHeight; + }; + + this.$eachHandler = function(value) { + this.$attrExcludePropBind = apf.extend({}, this.$attrExcludePropBind); + this.$attrExcludePropBind.value = value ? 2 : 0; + } + this.addEventListener("prop.each", this.$eachHandler); + + this.addEventListener("$clear", function(){ + this.$container.innerHTML = ""; + this.value = ""; + this.dispatchEvent("prop.value", {value: ""}); + }); + + // @todo replace this stub with something that does something + this.$moveNode = function() {}; + + /**** Public methods ****/ + + + + this.addValue = function(value){ + this.$propHandlers["value"].call(this, value, null, null, true); + this.dispatchEvent("prop.value", {value: this.value}); + } + + /** + * Sets the value of this element. This should be one of the values + * specified in the values attribute. + * @param {String} value the new value of this element + */ + this.setValue = function(value){ + this.setProperty("value", value, false, true); + }; + + /** + * Returns the current value of this element. + * @return {String} + */ + this.getValue = function(){ + return this.$container.innerHTML; + }; + + + + /**** Keyboard Support ****/ + + + this.addEventListener("keydown", function(e){ + var key = e.keyCode; + + switch (key) { + case 33: + //PGUP + this.$container.scrollTop -= this.$container.offsetHeight; + break; + case 34: + //PGDN + this.$container.scrollTop += this.$container.offsetHeight; + break; + case 35: + //END + this.$container.scrollTop = this.$container.scrollHeight; + break; + case 36: + //HOME + this.$container.scrollTop = 0; + break; + case 38: + this.$container.scrollTop -= 10; + break; + case 40: + this.$container.scrollTop += 10; + break; + default: + return; + } + + return false; + }, true); + + + /**** Private methods ****/ + + this.$canLoadData = function(){ + return this.$attrBindings.value ? true : false; + } + + this.$add = function(xmlNode, Lid, xmlParentNode, htmlParentNode, beforeNode){ + var f = this.$attrBindings.value.cvalue; + var html = f(xmlNode); + html = "
" + html + "
"; + if (htmlParentNode) { + if (beforeNode) + beforeNode.insertAdjacentHTML("beforebegin", html); + else + this.$container.insertAdjacentHTML("beforeend", html); + //apf.insertHtmlNode(oItem, htmlParentNode, beforeNode); + + //Iframe bug fix for IE (leaves screen white); + if (apf.cannotSizeIframe && this.oIframe) + this.oIframe.style.width = this.oIframe.offsetWidth + "px"; + + if (this.scrolldown && this.$scrolldown) + this.$scrollArea.scrollTop = this.$scrollArea.scrollHeight; + } + else + this.$nodes.push(html); + } + + this.$fill = function(){ + //apf.insertHtmlNode(null, this.$container, null, this.$nodes.join("")); + this.$container.insertAdjacentHTML("beforeend", this.$nodes.join("")); + this.$nodes = []; + } + + this.$deInitNode = + this.$updateNode = + this.$moveNode = apf.K; + + /**** Init ****/ + + this.$draw = function(){ + this.$ext = this.$getExternal(); + this.$container = this.$getLayoutNode("main", "container", this.$ext); + + if (apf.hasCssUpdateScrollbarBug && !apf.getStyle(this.$container, "padding")) + this.$fixScrollBug(); + + this.$scrollArea = this.oFocus ? this.oFocus.parentNode : this.$container; + + if (this.$container.tagName.toLowerCase() == "iframe") { + if (apf.isIE) { + this.oIframe = this.$container; + var iStyle = this.skin.selectSingleNode("iframe_style"); + this.oIframe.contentWindow.document.write( + "\ + \ + \ + \ + \ + "); + this.$container = this.oIframe.contentWindow.document.body; + } + else { + var node = document.createElement("div"); + this.$ext.parentNode.replaceChild(node, this.$ext); + node.className = this.$ext.className; + this.$ext = this.$container = node; + } + } + + if (this.getAttribute("each")) + this.$eachHandler(); + }; + + this.addEventListener("DOMNodeRemovedFromDocument", function() { + clearInterval(this.$textTimer); + apf.destroyHtmlNode(this.oDrag); + + if (this.$scrollArea) + this.$scrollArea.onscoll = this.$scrollArea = null; + + this.oDrag = this.oIframe = this.oFocus = this.$container = this.$ext = null; + }); +}).call(apf.text.prototype = new apf.MultiselectBinding()); + +apf.aml.setElement("text", apf.text); + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/elements/textbox.js)SIZE(27544)TIME(Fri, 14 Oct 2011 08:01:44 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + + +//@todo DOCUMENT the modules too + +/** + * Element displaying a rectangular area wich allows a + * user to type information. The information typed can be + * restricted by using this.$masking. The information can also + * be hidden from view when used in password mode. By adding an + * {@link element.autocomplete autocomplete element} as a child the + * value for the textbox can be looked up as you type. By setting the + * {@link element.textbox.attribute.mask mask atribute}, complex data input + * validation is done while the users types. + * + * @constructor + * @define input, secret, textarea, textbox + * @allowchild autocomplete, {smartbinding} + * @addnode elements + * + * @inherits apf.StandardBinding + * @inherits apf.XForms + * + * @author Ruben Daniels (ruben AT ajax DOT org) + * @version %I%, %G% + * @since 0.1 + * + * @binding value Determines the way the value for the element is retrieved + * from the bound data. + * Example: + * Sets the value based on data loaded into this component. + * + * + * + * + * + * + * Example: + * A shorter way to write this is: + * + * + * + * + * + * + * + * @event click Fires when the user presses a mousebutton while over this element and then let's the mousebutton go. + * @event mouseup Fires when the user lets go of a mousebutton while over this element. + * @event mousedown Fires when the user presses a mousebutton while over this element. + * @event keyup Fires when the user lets go of a keyboard button while this element is focussed. + * object: + * {Number} keyCode which key was pressed. This is an ascii number. + * @event clear Fires when the content of this element is cleared. + */ +apf.input = function(struct, tagName){ + this.$init(tagName || "input", apf.NODE_VISIBLE, struct); +}; + +apf.secret = function(struct, tagName){ + this.$init(tagName || "secret", apf.NODE_VISIBLE, struct); +}; + +apf.password = function(struct, tagName){ + this.$init(tagName || "password", apf.NODE_VISIBLE, struct); +}; + +apf.textarea = function(struct, tagName){ + this.$init(tagName || "textarea", apf.NODE_VISIBLE, struct); + + this.multiline = true; +}; + +// HTML5 email element +apf.email = function(struct, tagName){ + this.$init(tagName || "email", apf.NODE_VISIBLE, struct); +}; + +apf.textbox = function(struct, tagName){ + this.$init(tagName || "textbox", apf.NODE_VISIBLE, struct); +}; + +(function(){ + this.implement( + + apf.DataAction + + + ); + + this.$focussable = true; // This object can get the focus + this.$masking = false; + this.$autoComplete = false; + + this.$childProperty = "value"; + + //this.realtime = false; + this.value = ""; + this.readonly = false; + this.$isTextInput = true; + this.multiline = false; + + /** + * @attribute {Boolean} realtime whether the value of the bound data is + * updated as the user types it, or only when this element looses focus or + * the user presses enter. + */ + this.$booleanProperties["readonly"] = true; + this.$booleanProperties["focusselect"] = true; + this.$booleanProperties["realtime"] = true; + this.$supportedProperties.push("value", "mask", "initial-message", + "focusselect", "realtime", "type"); + + /** + * @attribute {String} value the text of this element + * @todo apf3.0 check use of this.$propHandlers["value"].call + */ + this.$propHandlers["value"] = function(value, prop, force, initial){ + if (!this.$input || !initial && this.getValue() == value) + return; + + // Set Value + if (!initial && !value && !this.hasFocus()) //@todo apf3.x research the use of clear + return this.$clear(); + else if (this.isHTMLBox) { + if (this.$input.innerHTML != value) + this.$input.innerHTML = value; + } + else if (this.$input.value != value) + this.$input.value = value; + + if (!initial) + apf.setStyleClass(this.$ext, "", [this.$baseCSSname + "Initial"]); + + if (this.$button) + this.$button.style.display = value && !initial ? "block" : "none"; + }; + + //See validation + //var oldPropHandler = this.$propHandlers["maxlength"]; + this.addEventListener("prop.maxlength", function(e){ + //Special validation support using nativate max-length browser support + if (this.$input.tagName.toLowerCase().match(/input|textarea/)) + this.$input.maxLength = parseInt(e.value) || null; + }); + + this.addEventListener("prop.editable", function(e){ + if (apf.isIE) + this.$input.unselectable = e.value ? "On" : "Off"; + else { + if (e.value) + apf.addListener(this.$input, "mousedown", apf.preventDefault); + else + apf.removeListener(this.$input, "mousedown", apf.preventDefault); + } + }); + + /** + * @attribute {String} mask a complex input pattern that the user should + * adhere to. This is a string which is a combination of special and normal + * characters. Then comma seperated it has two options. The first option + * specifies whether the non input characters (the chars not typed by the + * user) are in the value of this element. The second option specifies the + * character that is displayed when the user hasn't yet filled in a + * character. + * Characters: + * 0 Any digit + * 1 The number 1 or 2. + * 9 Any digit or a space. + * # User can enter a digit, space, plus or minus sign. + * L Any alpha character, case insensitive. + * ? Any alpha character, case insensitive or space. + * A Any alphanumeric character. + * a Any alphanumeric character or space. + * X Hexadecimal character, case insensitive. + * x Hexadecimal character, case insensitive or space. + * & Any whitespace. + * C Any character. + * ! Causes the input mask to fill from left to right instead of from right to left. + * ' The start or end of a literal part. + * " The start or end of a literal part. + * > Converts all characters that follow to uppercase. + * < Converts all characters that follow to lowercase. + * \ Cancel the special meaning of a character. + * Example: + * An american style phone number. + * + * + * + * Example: + * A dutch postal code + * + * + * + * Example: + * A date + * + * + * + * Example: + * A serial number + * + * + * + * Example: + * A MAC address + * + * + * + */ + this.$propHandlers["mask"] = function(value){ + if (this.mask.toLowerCase() == "password")// || !apf.hasMsRangeObject) + return; + + if (!value) { + throw new Error("Not Implemented"); + } + + if (!this.$masking) { + this.$masking = true; + this.implement(apf.textbox.masking); + this.focusselect = false; + //this.realtime = false; + } + + this.setMask(this.mask); + }; + + //this.$propHandlers["ref"] = function(value) { + // this.$input.setAttribute("name", value.split("/").pop().split("::").pop() + // .replace(/[\@\.\(\)]*/g, "")); + //}; + + /** + * @attribute {String} initial-message the message displayed by this element + * when it doesn't have a value set. This property is inherited from parent + * nodes. When none is found it is looked for on the appsettings element. + */ + this.$propHandlers["initial-message"] = function(value){ + if (value) { + + if (apf.hasFocusBug) + this.$input.onblur(); + + + //this.$propHandlers["value"].call(this, value, null, true); + } + + if (!this.value) + this.$clear(true); + + if (this.type == "password" && this.$inputInitFix) { + this.$inputInitFix.innerHTML = value; + apf.setStyleClass(this.$inputInitFix, "initFxEnabled"); + } + }; + + /** + * @attribute {Boolean} focusselect whether the text in this element is + * selected when this element receives focus. + */ + this.$propHandlers["focusselect"] = function(value){ + var _self = this; + this.$input.onmousedown = function(){ + _self.focusselect = false; + }; + + this.$input.onmouseup = + this.$input.onmouseout = function(){ + _self.focusselect = value; + }; + }; + + /** + * @attribute {String} type the type or function this element represents. + * This can be any arbitrary name. Although there are some special values. + * Possible values: + * username this element is used to type in the name part of login credentials. + * password this element is used to type in the password part of login credentials. + */ + this.$propHandlers["type"] = function(value){ + if (value && "password|username".indexOf(value) > -1 + && typeof this.focusselect == "undefined") { + this.focusselect = true; + this.$propHandlers["focusselect"].call(this, true); + } + }; + + this.$isTextInput = function(e){ + return true; + }; + + /**** Public Methods ****/ + + + + /** + * Sets the value of this element. This should be one of the values + * specified in the values attribute. + * @param {String} value the new value of this element + */ + this.setValue = function(value){ + return this.setProperty("value", value, false, true); + }; + + this.clear = function(){ + this.setProperty("value", ""); + } + + //@todo cleanup and put initial-message behaviour in one location + this.$clear = function(noEvent){ + if (this["initial-message"]) { + apf.setStyleClass(this.$ext, this.$baseCSSname + "Initial"); + this.$propHandlers["value"].call(this, this["initial-message"], null, null, true); + } + else { + this.$propHandlers["value"].call(this, "", null, null, true); + } + + if (!noEvent) + this.dispatchEvent("clear");//@todo this should work via value change + } + + /** + * Returns the current value of this element. + * @return {String} + */ + this.getValue = function(){ + var v = this.isHTMLBox ? this.$input.innerHTML : this.$input.value; + return v == this["initial-message"] ? "" : v.replace(/\r/g, ""); + }; + + + + /** + * Selects the text in this element. + */ + this.select = function(){ + try { + this.$input.select(); + } + catch(e){} + }; + + /** + * Deselects the text in this element. + */ + this.deselect = function(){this.$input.deselect();}; + + /**** Private Methods *****/ + + this.$enable = function(){this.$input.disabled = false;}; + this.$disable = function(){this.$input.disabled = true;}; + + this.$insertData = function(str){ + return this.setValue(str); + }; + + /** + * @private + */ + this.insert = function(text){ + if (apf.hasMsRangeObject) { + try { + this.$input.focus(); + } + catch(e) {} + var range = document.selection.createRange(); + if (this.oninsert) + text = this.oninsert(text); + range.pasteHTML(text); + range.collapse(true); + range.select(); + } + else { + this.$input.value += text; + } + }; + + this.addEventListener("$clear", function(){ + this.value = "";//@todo what about property binding? + + if (this["initial-message"] && apf.document.activeElement != this) { + this.$propHandlers["value"].call(this, this["initial-message"], null, null, true); + apf.setStyleClass(this.$ext, this.$baseCSSname + "Initial"); + } + else { + this.$propHandlers["value"].call(this, ""); + } + + if (!this.$input.tagName.toLowerCase().match(/input|textarea/i)) { + if (apf.hasMsRangeObject) { + try { + var range = document.selection.createRange(); + range.moveStart("sentence", -1); + //range.text = ""; + range.select(); + } + catch(e) {} + } + } + + this.dispatchEvent("clear"); //@todo apf3.0 + }); + + this.$keyHandler = function(key, ctrlKey, shiftKey, altKey, e){ + if (this.$button && key == 27) { + //this.$clear(); + if (this.value) { + this.change(""); + e.stopPropagation(); + } + //this.focus({mouse:true}); + } + + /*if (this.dispatchEvent("keydown", { + keyCode : key, + ctrlKey : ctrlKey, + shiftKey : shiftKey, + altKey : altKey, + htmlEvent : e}) === false) + return false; + + // @todo: revisit this IF statement - dead code? + if (false && apf.isIE && (key == 86 && ctrlKey || key == 45 && shiftKey)) { + var text = window.clipboardData.getData("Text"); + if ((text = this.dispatchEvent("keydown", { + text : this.onpaste(text)}) === false)) + return false; + if (!text) + text = window.clipboardData.getData("Text"); + + this.$input.focus(); + var range = document.selection.createRange(); + range.text = ""; + range.collapse(); + range.pasteHTML(text.replace(/\n/g, "
").replace(/\t/g, "   ")); + + return false; + }*/ + }; + + this.$registerElement = function(oNode) { + if (!oNode) return; + if (oNode.localName == "autocomplete") + this.$autoComplete = oNode; + }; + + var fTimer; + this.$focus = function(e){ + if (!this.$ext || this.$ext.disabled) + return; + + this.$setStyleClass(this.$ext, this.$baseCSSname + "Focus"); + + if (this["initial-message"] && this.$input.value == this["initial-message"]) { + this.$propHandlers["value"].call(this, "", null, null, true); + apf.setStyleClass(this.$ext, "", [this.$baseCSSname + "Initial"]); + } + + var _self = this; + function delay(){ + try { + if (!fTimer || document.activeElement != _self.$input) { + _self.$input.focus(); + } + else { + clearInterval(fTimer); + return; + } + } + catch(e) {} + + if (_self.$masking) + _self.setPosition(); + + if (_self.focusselect) + _self.select(); + }; + + if ((!e || e.mouse) && apf.isIE) { + clearInterval(fTimer); + fTimer = setInterval(delay, 1); + } + else + delay(); + }; + + this.$blur = function(e){ + if (!this.$ext) + return; + + if (!this.realtime) + this.change(this.getValue()); + + if (e) + e.cancelBubble = true; + + this.$setStyleClass(this.$ext, "", [this.$baseCSSname + "Focus", "capsLock"]); + + if (this["initial-message"] && this.$input.value == "") { + this.$propHandlers["value"].call(this, this["initial-message"], null, null, true); + apf.setStyleClass(this.$ext, this.$baseCSSname + "Initial"); + } + + /*if (apf.hasMsRangeObject) { + var r = this.$input.createTextRange(); + r.collapse(); + r.select(); + }*/ + + try { + if (apf.isIE || !e || e.srcElement != apf.window) + this.$input.blur(); + } + catch(e) {} + + // check if we clicked on the oContainer. ifso dont hide it + if (this.oContainer) { + $setTimeout("var o = apf.lookup(" + this.$uniqueId + ");\ + o.oContainer.style.display = 'none'", 100); + } + + clearInterval(fTimer); + }; + + /**** Init ****/ + + this.$draw = function(){ + var _self = this, + typedBefore = false; + + + if (this.localName == "codeeditor") { + this.skin = "textarea"; + this.$loadSkin(); + } + + + //Build Main Skin + this.$ext = this.$getExternal(null, null, function(oExt){ + var mask = this.getAttribute("mask"); + + if ((typeof mask == "string" && mask.toLowerCase() == "password") + || "secret|password".indexOf(this.localName) > -1) { + this.type = "password"; + this.$getLayoutNode("main", "input").setAttribute("type", "password"); + } + + + else if (this.localName == "email") { + this.datatype = (this.prefix ? this.prefix + ":" : "") + "email"; + this.$propHandlers["datatype"].call(this, this.datatype, "datatype"); + } + else if (this.localName == "url") { + this.datatype = (this.prefix ? this.prefix + ":" : "") + "url"; + this.$propHandlers["datatype"].call(this, this.datatype, "datatype"); + } + + + oExt.setAttribute("onmousedown", "if (!this.host.disabled) \ + this.host.dispatchEvent('mousedown', {htmlEvent : event});"); + oExt.setAttribute("onmouseup", "if (!this.host.disabled) \ + this.host.dispatchEvent('mouseup', {htmlEvent : event});"); + oExt.setAttribute("onclick", "if (!this.host.disabled) \ + this.host.dispatchEvent('click', {htmlEvent : event});"); + }); + this.$input = this.$getLayoutNode("main", "input", this.$ext); + this.$button = this.$getLayoutNode("main", "button", this.$ext); + this.$inputInitFix = this.$getLayoutNode("main", "initialfix", this.$ext); + + if (this.type == "password") + this.$propHandlers["type"].call(this, "password"); + + if (!apf.hasContentEditable && "input|textarea".indexOf(this.$input.tagName.toLowerCase()) == -1) { + var node = this.$input; + this.$input = node.parentNode.insertBefore(document.createElement("textarea"), node); + node.parentNode.removeChild(node); + this.$input.className = node.className; + if (this.$ext == node) + this.$ext = this.$input; + } + + if (this.$button) { + this.$button.onmousedown = function(){ + _self.$clear(); //@todo why are both needed for doc filter + _self.change(""); //@todo only this one should be needed + _self.focus({mouse:true}); + } + } + + //@todo for skin switching this should be removed + if (this.$input.tagName.toLowerCase() == "textarea") { + this.addEventListener("focus", function(e){ + //if (this.multiline != "optional") + //e.returnValue = false + }); + } + + this.$input.onselectstart = function(e){ + if (!e) e = event; + e.cancelBubble = true; + } + this.$input.host = this; + + this.$input.onkeydown = function(e){ + e = e || window.event; + + if (this.host.disabled) { + e.returnValue = false; + return false; + } + + //Change + if (!_self.realtime) { + var value = _self.getValue(); + if (e.keyCode == 13 && value != _self.value) + _self.change(value); + } + else if (apf.isWebkit && _self.xmlRoot && _self.getValue() != _self.value) //safari issue (only old??) + $setTimeout("var o = apf.lookup(" + _self.$uniqueId + ");\ + o.change(o.getValue())"); + + if (_self.readonly || _self.multiline == "optional" && e.keyCode == 13 && !e.shiftKey + || e.ctrlKey && (e.keyCode == 66 || e.keyCode == 73 + || e.keyCode == 85)) { + e.returnValue = false; + return false; + } + + if (typedBefore && this.getAttribute("type") == "password" && this.value != "") { + var hasClass = (_self.$ext.className.indexOf("capsLock") > -1), + capsKey = (e.keyCode === 20); + if (capsKey) // caps off + apf.setStyleClass(_self.$ext, hasClass ? null : "capsLock", hasClass ? ["capsLock"] : null); + } + + //Autocomplete + if (_self.$autoComplete || _self.oContainer) { + var keyCode = e.keyCode; + $setTimeout(function(){ + if (_self.$autoComplete) + _self.$autoComplete.fillAutocomplete(keyCode); + else + _self.fillAutocomplete(keyCode); + }); + } + + //Non this.$masking + if (!_self.mask) { + return _self.$keyHandler(e.keyCode, e.ctrlKey, + e.shiftKey, e.altKey, e); + } + }; + + this.$input.onkeyup = function(e){ + if (!e) + e = event; + + if (this.host.disabled) + return false; + + var keyCode = e.keyCode; + + if (_self.$button) + _self.$button.style.display = this.value ? "block" : "none"; + + if (_self.realtime) { + $setTimeout(function(){ + var v; + if (!_self.mask && (v = _self.getValue()) != _self.value) + _self.change(v); + if (apf.isIE) + _self.dispatchEvent("keyup", {keyCode : keyCode});//@todo + }); + } + else if (apf.isIE) { + _self.dispatchEvent("keyup", {keyCode : keyCode});//@todo + } + + + if (_self.isValid && _self.isValid() && e.keyCode != 13 && e.keyCode != 17) + _self.clearError(); + + }; + + + if (apf.hasFocusBug) + apf.sanitizeTextbox(this.$input); + + + if (apf.hasAutocompleteXulBug) + this.$input.setAttribute("autocomplete", "off"); + + if ("INPUT|TEXTAREA".indexOf(this.$input.tagName) == -1) { + this.isHTMLBox = true; + + this.$input.unselectable = "Off"; + this.$input.contentEditable = true; + this.$input.style.width = "1px"; + + this.$input.select = function(){ + var r = document.selection.createRange(); + r.moveToElementText(this); + r.select(); + } + }; + + this.$input.deselect = function(){ + if (!document.selection) return; + + var r = document.selection.createRange(); + r.collapse(); + r.select(); + }; + + var f; + apf.addListener(this.$input, "keypress", f = function(e) { + if (_self.$input.getAttribute("type") != "password") + return apf.removeListener(_self.$input, "keypress", f); + e = e || window.event; + // get key pressed + var which = -1; + if (e.which) + which = e.which; + else if (e.keyCode) + which = e.keyCode; + + // get shift status + var shift_status = false; + if (e.shiftKey) + shift_status = e.shiftKey; + else if (e.modifiers) + shift_status = !!(e.modifiers & 4); + + if (((which >= 65 && which <= 90) && !shift_status) || + ((which >= 97 && which <= 122) && shift_status)) { + // uppercase, no shift key + apf.setStyleClass(_self.$ext, "capsLock"); + } + else { + apf.setStyleClass(_self.$ext, null, ["capsLock"]); + } + typedBefore = true; + }); + }; + + this.$loadAml = function() { + if (typeof this["initial-message"] == "undefined") + this.$setInheritedAttribute("initial-message"); + + if (typeof this.realtime == "undefined") + this.$setInheritedAttribute("realtime"); + } + + this.addEventListener("DOMNodeRemovedFromDocument", function(){ + if (this.$button) + this.$button.onmousedown = null; + + if (this.$input) { + this.$input.onkeypress = + this.$input.onmouseup = + this.$input.onmouseout = + this.$input.onmousedown = + this.$input.onkeydown = + this.$input.onkeyup = + this.$input.onselectstart = null; + } + }); + +}).call(apf.textbox.prototype = new apf.StandardBinding()); + + +apf.config.$inheritProperties["initial-message"] = 1; +apf.config.$inheritProperties["realtime"] = 1; + +apf.input.prototype = +apf.secret.prototype = +apf.password.prototype = +apf.textarea.prototype = +apf.email.prototype = apf.textbox.prototype; + +apf.aml.setElement("input", apf.input); +apf.aml.setElement("secret", apf.secret); +apf.aml.setElement("password", apf.password); +apf.aml.setElement("textarea", apf.textarea); +apf.aml.setElement("textbox", apf.textbox); + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/elements/toc.js)SIZE(8342)TIME(Fri, 14 Oct 2011 08:01:44 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/elements/toolbar.js)SIZE(2787)TIME(Fri, 14 Oct 2011 08:01:44 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + +/** + * Element displaying a bar containing buttons and other aml elements. + * This element is usually positioned in the top of an application allowing + * the user to choose from grouped buttons. + * Example: + * + * + * About us + * Help + * + * + * Tutorials + * Live Helps + * + * Visit Ajax.org + * Exit + * + * + * + * + * File + * Edit + * + * + * + * + * + * @constructor + * @define toolbar + * @addnode elements + * @allowchild bar, menubar + * + * @author Ruben Daniels (ruben AT ajax DOT org) + * @version %I%, %G% + * @since 0.4 + * + * @inherits apf.Presentation + */ + +apf.toolbar = function(struct, tagName){ + this.$init(tagName || "toolbar", apf.NODE_VISIBLE, struct); +}; + +(function(){ + this.$focussable = false; + + /**** DOM Hooks ****/ + + + /**** Init ****/ + + this.$draw = function(){ + //Build Main Skin + this.$ext = this.$getExternal(); + this.$int = this.$getLayoutNode("main", "container", this.$ext); + }; +}).call(apf.toolbar.prototype = new apf.Presentation()); + +apf.aml.setElement("toolbar", apf.toolbar); + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/elements/tree.js)SIZE(17445)TIME(Tue, 13 Dec 2011 13:33:58 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + + +/** + * Element displaying data in a list where each item in the list can contain + * such a list. This element gives the user the ability to walk through this + * tree of data by clicking open elements to show more elements. The tree + * can grow by fetching more data when the user requests it. + * Example: + * A tree with inline items. + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * Example: + * + * + * + * + * + * + * + * + * Example: + * Inline tree description that draws the same as the above example: + * + * + * + * + * @constructor + * @define tree + * @allowchild {smartbinding} + * @addnode elements + * + * @author Ruben Daniels (ruben AT ajax DOT org) + * @version %I%, %G% + * @since 0.4 + * + * @binding insert Determines how new data is loaded when the user expands + * an item. For instance by clicking on the + button. This way only the root nodes + * need to be loaded at the start of the application. All other children are + * received on demand when the user requests it by navigating throught the tree. + * Example: + * This example shows an insert rule that only works on folder elements. It will + * read the directory contents using webdav and insert it under the selected + * tree node. + * + * + * + * + * + * + * + * + * + * @attribute {String} get the {@link term.datainstruction data instruction} that is used to load the new data. + * @binding caption Determines the caption of a tree node. + * @binding icon Determines the icon of a tree node. + * @binding css Determines a css class for a tree node. + * Example: + * In this example a node is bold when the folder contains unread messages: + * + * + * + * + * + * + * + * + * + * @binding tooltip Determines the tooltip of a tree node. + * @binding empty Determines the empty message of a node. + * Example: + * This example shows a gouped contact list, that displays a message under + * empty groups. + * + * + * + * + * + * + * + * + * + */ +apf.tree = function(struct, tagName){ + this.$init(tagName || "tree", apf.NODE_VISIBLE, struct); +}; + +(function(){ + var HAS_CHILD = 1 << 1, + IS_CLOSED = 1 << 2, + IS_LAST = 1 << 3, + IS_ROOT = 1 << 4, + treeState = this.$treeState; + + /**** Properties and Attributes ****/ + + + + /** + * @attribute {String} mode Sets the way this element interacts with the user. + * Possible values: + * check the user can select a single item from this element. The selected item is indicated. + * radio the user can select multiple items from this element. Each selected item is indicated. + */ + this.$mode = 0; + this.$propHandlers["mode"] = function(value){ + if ("check|radio".indexOf(value) > -1) { + if (!this.hasFeature(apf.__MULTICHECK__)) + this.implement(apf.MultiCheck); + + this.addEventListener("afterrename", $afterRenameMode); //what does this do? + + this.multicheck = value == "check"; //radio is single + this.$mode = this.multicheck ? 1 : 2; + } + else { + //@todo undo actionRules setting + this.removeEventListener("afterrename", $afterRenameMode); + //@todo unimplement?? + this.$mode = 0; + } + }; + + //@todo apf3.0 retest this completely + function $afterRenameMode(){ + + } + + + this.$initNode = function(xmlNode, state, Lid){ + //Setup Nodes Interaction + this.$getNewContext("item"); + + var hasChildren = state & HAS_CHILD || this.emptyMessage + && this.$applyBindRule("empty", xmlNode), + //should be restructured and combined events set per element + oItem = this.$getLayoutNode("item"); + //@todo this should use dispatchEvent, and be moved to oExt + oItem.setAttribute("onmouseover", + "var o = apf.lookup(" + this.$uniqueId + ");\ + o.$setStyleClass(this, 'hover', null, true);"); + oItem.setAttribute("onmouseout", + "var o = apf.lookup(" + this.$uniqueId + ");\ + o.$setStyleClass(this, '', ['hover'], true);"); + /*oItem.setAttribute("onmousedown", + "var o = apf.lookup(" + this.$uniqueId + ");\ + if (o.onmousedown) o.onmousedown(event, this);");*/ + + //Set open/close skin class & interaction + this.$setStyleClass(this.$getLayoutNode("item", "class"), treeState[state]).setAttribute(apf.xmldb.htmlIdTag, Lid); + this.$setStyleClass(this.$getLayoutNode("item", "container"), treeState[state]) + //this.$setStyleClass(oItem, xmlNode.tagName) + var elOpenClose = this.$getLayoutNode("item", "openclose"); + if (elOpenClose) { //hasChildren && + elOpenClose.setAttribute("children", hasChildren); + elOpenClose.setAttribute("onmousedown", + "if (this.getAttribute('children') == false) return;\ + var o = apf.lookup(" + this.$uniqueId + ");\ + o.slideToggle(this, null, null, true);\ + apf.cancelBubble(event, o);"); + + elOpenClose.setAttribute("ondblclick", "event.cancelBubble = true"); + } + + + if (this.$mode) { + var elCheck = this.$getLayoutNode("item", "check"); + if (elCheck) { + elCheck.setAttribute("onmousedown", + "var o = apf.lookup(" + this.$uniqueId + ");\ + o.checkToggle(this, true);\o.$skipSelect = true;"); + + if (apf.isTrue(this.$applyBindRule("checked", xmlNode))) { + this.$checkedList.push(xmlNode); + this.$setStyleClass(oItem, "checked"); + } + else if (this.isChecked(xmlNode)) + this.$setStyleClass(oItem, "checked"); + } + else { + + return false; + } + } + + + var ocAction = this.opencloseaction || "ondblclick"; + + //Icon interaction + var elIcon = this.$getLayoutNode("item", "icon"); + if (elIcon && elIcon != elOpenClose) { + if (ocAction != "ondblclick") { + elIcon.setAttribute(ocAction, + "var o = apf.lookup(" + this.$uniqueId + ");" + + (ocAction == "onmousedown" ? "o.select(this, apf.getCtrlKey(event), event.shiftKey, event.button);" : "") + + (true ? "o.slideToggle(this, null, null, true);" : "")); + } + if (ocAction != "onmousedown") { + elIcon.setAttribute("onmousedown", + "apf.lookup(" + this.$uniqueId + ").select(this, apf.getCtrlKey(event), event.shiftKey, event.button);"); + } + + elIcon.setAttribute("ondblclick", + "var o = apf.lookup(" + this.$uniqueId + ");\ + o.choose(null, true);" + + + "o.stopRename();" + + + (true && !ocAction == "ondblclick" ? "o.slideToggle(this, null, null, true);" : "") + + "apf.cancelBubble(event,o);"); + } + + //Select interaction + var elSelect = this.$getLayoutNode("item", "select"), + strMouseDown; + + + if (this.hasFeature(apf.__RENAME__) || this.hasFeature(apf.__DRAGDROP__)) { + strMouseDown = + 'var o = apf.lookup(' + this.$uniqueId + ');\ + var xmlNode = apf.xmldb.findXmlNode(this);\ + var isSelected = o.isSelected(xmlNode);\ + this.hasPassedDown = true;\ + if (event.button == 2) \ + o.stopRename();\ + else if (!o.renaming && o.hasFocus() && isSelected == 1) \ + this.dorename = true;\ + if (!o.hasFeature(apf.__DRAGDROP__) || !isSelected && !apf.getCtrlKey(event))\ + o.select(this, apf.getCtrlKey(event), event.shiftKey, event.button);\ + apf.cancelBubble(event, o);'; + + elSelect.setAttribute("onmouseout", 'this.hasPassedDown = false;' + (elSelect.getAttribute("onmouseout") || "")); + elSelect.setAttribute("onmouseup", 'if (!this.hasPassedDown) return;\ + var o = apf.lookup(' + this.$uniqueId + ');' + + + 'if (this.dorename && !o.mode)\ + o.startDelayedRename(event, null, true);' + + + 'this.dorename = false;\ + var xmlNode = apf.xmldb.findXmlNode(this);\ + var isSelected = o.isSelected(xmlNode);\ + if (o.hasFeature(apf.__DRAGDROP__))\ + o.select(this, apf.getCtrlKey(event), event.shiftKey, event.button);'); + } + else + + { + strMouseDown = "o.select(this, apf.getCtrlKey(event), event.shiftKey, event.button);\ + apf.cancelBubble(event, o);"; + } + + if (ocAction != "ondblclick") { + elSelect.setAttribute(ocAction, + "var o = apf.lookup(" + this.$uniqueId + ");" + + (ocAction == "onmousedown" ? strMouseDown : "") + + (true ? "o.slideToggle(this, null, null, true);" : "")); + } + if (ocAction != "onmousedown") { + elSelect.setAttribute("onmousedown", + "var o = apf.lookup(" + this.$uniqueId + ");" + strMouseDown); + } + + elSelect.setAttribute("ondblclick", + "var o = apf.lookup(" + this.$uniqueId + ");\ + o.choose(null, true);" + + + "o.stopRename();this.dorename=false;" + + + (ocAction == "ondblclick" ? "o.slideToggle(this, null, null, true);" : "") + + "apf.cancelBubble(event, o);"); + + //Setup Nodes Identity (Look) + if (elIcon) { + var iconURL = this.$applyBindRule("icon", xmlNode); + if (iconURL) { + if (elIcon.tagName.match(/^img$/i)) + elIcon.setAttribute("src", apf.getAbsolutePath(this.iconPath, iconURL)); + else + elIcon.setAttribute("style", "background-image:url(" + + apf.getAbsolutePath(this.iconPath, iconURL) + ")"); + } + } + + var elCaption = this.$getLayoutNode("item", "caption"); + if (elCaption) { + + if (elCaption.nodeType == 1 + && this.$cbindings.caption && this.$cbindings.caption.hasAml){ + var q = (this.$amlBindQueue || (this.$amlBindQueue = {})); + + elCaption.setAttribute("id", "placeholder_" + this.$uniqueId + + "_" + ((q.caption || (q.caption = [])).push(xmlNode) - 1)); + apf.setNodeValue(elCaption, ""); + } + else + + { + apf.setNodeValue(elCaption, + this.$applyBindRule("caption", xmlNode)); + } + } + + var strTooltip = this.$applyBindRule("tooltip", xmlNode) + if (strTooltip) + oItem.setAttribute("title", strTooltip); + + + var cssClass = this.$applyBindRule("css", xmlNode); + if (cssClass) { + this.$setStyleClass(this.$getLayoutNode("item", null, oItem), cssClass); + this.$setStyleClass(this.$getLayoutNode("item", "container", oItem), cssClass); + this.$dynCssClasses.push(cssClass); + } + + + return oItem; + }; + + this.$updateNode = function(xmlNode, htmlNode){ + var elIcon = this.$getLayoutNode("item", "icon", htmlNode), + iconURL = this.$applyBindRule("icon", xmlNode); + if (elIcon && iconURL) { + if (elIcon.tagName && elIcon.tagName.match(/^img$/i)) + elIcon.src = apf.getAbsolutePath(this.iconPath, iconURL); + else + elIcon.style.backgroundImage = "url(" + + apf.getAbsolutePath(this.iconPath, iconURL) + ")"; + } + + + //@todo + + + var elCaption = this.$getLayoutNode("item", "caption", htmlNode); + if (elCaption) { + //if (elCaption.nodeType != 1) + //elCaption = elCaption.parentNode; + + if (elCaption.nodeType == 1) { + + if (!this.$cbindings.caption || !this.$cbindings.caption.hasAml) + + elCaption.innerHTML = this.$applyBindRule("caption", xmlNode); + } + else + elCaption.nodeValue = this.$applyBindRule("caption", xmlNode); + } + + var strTooltip = this.$applyBindRule("tooltip", xmlNode); + if (strTooltip) + htmlNode.setAttribute("title", strTooltip); + + + var cssClass = this.$applyBindRule("css", xmlNode); + if (cssClass || this.$dynCssClasses.length) { + this.$setStyleClass(htmlNode, cssClass, this.$dynCssClasses); //@todo overhead! + if (cssClass && !this.$dynCssClasses.contains(cssClass)) + this.$dynCssClasses.push(cssClass); + } + + }; + + /**** Init ****/ + + this.$draw = function(){ + this.$drawBase(); + }; +}).call(apf.tree.prototype = new apf.BaseTree()); + +apf.aml.setElement("tree", apf.tree); + +apf.aml.setElement("checked", apf.BindingRule); + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/elements/upload.js)SIZE(28994)TIME(Fri, 14 Oct 2011 08:01:44 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/elements/vectorflow.js)SIZE(65716)TIME(Fri, 14 Oct 2011 08:01:44 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/elements/video.js)SIZE(20319)TIME(Fri, 14 Oct 2011 08:01:44 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/elements/viewport.js)SIZE(1796)TIME(Fri, 14 Oct 2011 08:01:44 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/elements/webdav.js)SIZE(50239)TIME(Tue, 06 Dec 2011 16:22:54 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + + +/** + * Element implementing WebDAV remote filesystem protocol. + * WebDAV stands for "Web-based Distributed Authoring and Versioning". It is a + * set of extensions to the HTTP protocol which allows users to collaboratively + * edit and manage files on remote web servers (from: {@link http://www.webdav.org}. + * This Object aims to be a complete implementation of {@link http://www.webdav.org/specs/rfc4918.html RFC4981} + * and provides a scriptable interface to any WebDAV server. + * Example: + * Writing to a file with a WebDAV connector + * + * + * + * + * // write the text 'bar' to a file on the server called 'foo.txt' + * myWebDAV.writeFile('http://my-webdav-server.com/dav_files/foo.txt', 'bar'); + * + * + * + * Remarks: + * Calls can be made to a server using a special {@link term.datainstruction data instruction} + * format. + * + * get="{myWebdav.authenticate([@username], [@password])}" + * get="{myWebdav.login(...alias for authenticate...)}" + * set="{myWebdav.logout()}" + * get="{myWebdav.read([@path])}" + * get="{myWebdav.create([@path], 'New File.txt', '')}" + * set="{myWebdav.write([@path], [@data])}" + * set="{myWebdav.store(...alias for write...)}" + * set="{myWebdav.save(...alias for write...)}" + * set="{myWebdav.copy([@path], [../@path])}" + * set="{myWebdav.cp(...alias for copy...)}" + * set="{myWebdav.rename([@name], [@path])}" + * set="{myWebdav.move([@path], [../@path])}" + * set="{myWebdav.mv(...alias for move...)}" + * set="{myWebdav.remove([@path])}" + * set="{myWebdav.rmdir(...alias for remove...)}" + * set="{myWebdav.rm(...alias for remove...)}" + * set="{myWebdav.mkdir([@path])}" + * get="{myWebdav.readdir([@path])}" + * get="{myWebdav.scandir(...alias for readdir...)}" + * load="{myWebdav.getroot()}" + * set="{myWebdav.lock([@path])}" + * set="{myWebdav.unlock([@path])}" + * + * + * @event authfailure Fires when the authentication process failed or halted. + * bubbles: yes + * cancelable: Prevents an authentication failure to be thrown + * object: + * {String} username the username used for the login attempt + * {String} server the server address (URI) of the XMPP server + * {String} message a more detailed explanation of the error + * @event connectionerror Fires when the connection with the WebDAV server dropped. + * bubbles: yes + * cancelable: Prevents an connection error to be thrown + * object: + * {String} username the username used for the last-active session + * {String} server the server address (URI) of the WebDAV server + * {String} message a more detailed explanation of the error + * @event onfilecontents Fires when a {@link teleport.webdav.method.read read} request has + * completed successfully and returns the content-body of the requested file + * bubbles: yes + * object: + * {String} data the ASCI representation of the file content-body + * + * @define webdav + * @addnode teleport + * + * @author Mike de Boer + * @version %I%, %G% + * @since 1.0 + * @constructor + * + * @inherits apf.Class + * @inherits apf.BaseComm + * @inherits apf.http + * @namespace apf + * + * @default_private + */ + +apf.webdav = function(struct, tagName){ + this.$init(tagName || "webdav", apf.NODE_HIDDEN, struct); + + this.$locks = {}; + this.$lockedStack = []; + this.$lockId = 0; + this.$serverVars = {}; + this.$fsCache = {}; +}; + +(function() { + this.useHTTP = true; + this.method = "GET"; + + this.$showHidden = false; + + /** + * @attribute {String} [show-hidden] Flag that specifies if hidden files + * should be passed + */ + this.$booleanProperties["showhidden"] = true; + this.$supportedProperties.push("showhidden", "force-host"); + + this.$propHandlers["showhidden"] = function(value) { + this.$showHidden = value; + }; + + /* + * Simple helper function to store session variables in the private space. + * + * @param {String} name + * @param {mixed} value + * @type {mixed} + * @private + */ + this.$regVar = function(name, value) { + this.$serverVars[name] = value; + + return value; + }; + + /* + * Simple helper function to complete remove variables that have been + * stored in the private space by register() + * + * @param {String} name + * @type {void} + * @private + */ + function unregister() { + for (var i = 0; i < arguments.length; i++) { + if (typeof this.$serverVars[arguments[i]] != "undefined") { + this.$serverVars[arguments[i]] = null; + delete this.$serverVars[arguments[i]]; + } + } + } + + /* + * Simple helper function that retrieves a variable, stored in the private + * space. + * + * @param {String} name + * @type {mixed} + * @private + */ + this.$getVar = function(name) { + return this.$serverVars[name] || ""; + }; + + /** + * Wrapper function that handles and executes each HTTP request. It also takes + * care of streams that are incomplete or different than usual, thus produce + * invalid XML that needs to be tidied prior to processing. + * It also takes care of processing authentication process/ negotiation + * + * @param {Function} fCallback Function to execute when the request was successful + * @param {String} sPath Path to the WebDAV resource + * @param {sBody} [sBody] Optional body text (used for PUTs, for example) + * @param {Object} [oHeaders] Additional headers in key: value format + * @param {Boolean} [bUseXml] Tells the function whether to return XML. Defaults to FALSE + * @param {Object} [oBinary] Object with properties for binary upload in modern browsers + * @param {Function} [fCallback2] Optional second callback, passed to fCallback as arguments. Used mainly by the data instructions + * @type {void} + */ + this.doRequest = function(fCallback, sPath, sBody, oHeaders, bUseXml, oBinary, fCallback2) { + if (!this.$getVar("authenticated")) { + return onAuth.call(this, { + method : this.doRequest, + context: this, + args : arguments + }); + } + + if (bUseXml) { + if (!oHeaders) + oHeaders = {}; + oHeaders["Content-type"] = "text/xml; charset=utf-8"; + } + + var fHost = this["force-host"]; + if (fHost && sPath.indexOf(fHost) === -1) + sPath = fHost.replace(/[\/]+$/, "") + "/" + sPath.replace(/^[\/]+/, ""); + + var _self = this; + return this.get(sPath || this.$server, { + callback: function(data, state, extra) { + if (state != apf.SUCCESS) { + var oError; + + oError = WebDAVError.call(_self, "Url: " + extra.url + "\nInfo: " + extra.message); + + //if (extra.tpModule.retryTimeout(extra, state, _self, oError) === true) + // return true; + if (fCallback) + return fCallback.call(_self, data, state, extra, fCallback2); + else + throw oError; + } + + extra.headers = oHeaders; + + var iStatus = parseInt(extra.status); + if (iStatus == 401) //authentication requested + return; // 401's are handled by the browser already, so no need for additional processing... + + var sResponse = (extra.http.responseText || ""); + if (sResponse.replace(/^[\s\n\r]+|[\s\n\r]+$/g, "") != "" + && sResponse.indexOf('"} + : null, true); + }; + + /** + * Reads the properties of a resource on the server. + * see {@link teleport.webdav.method.getProperties} + * + * @param {String} sPath Path to the resource on the WebDAV server + * @param {Function} callback Function to execute when the request was successful + * @type {void} + */ + this.list = function(sPath, callback) { + return this.getProperties(sPath, 0, callback); + }; + + /** + * Write new contents (plaintext) to a file resource on the server, with or + * without an existing lock on the resource. + * + * @param {String} sPath Path to the file on the WebDAV server + * @param {String} sContent New content-body of the file + * @param {Boolean} [bLock] Whether to require a lock before write + * @param {Object} [oBinary] Object with properties for binary upload in modern browsers + * @param {Function} callback Function to execute when the request was successful + * @type {void} + */ + this.writeFile = + this.write = function(sPath, sContent, bLock, oBinary, callback) { + if (bLock) { + var oLock = this.lock(sPath); + if (!oLock.token) + return updateLockedStack.call(this, oLock, "write", arguments); + } + // binary option has been added later, keep fallback possible + if (typeof callback == "undefined" && typeof oBinary == "function") { + callback = oBinary; + oBinary = null; + } + + this.method = "PUT"; + var _self = this; + this.doRequest(function(data, state, extra) { + var iStatus = parseInt(extra.status); + if (state != apf.SUCCESS) { + var oError = WebDAVError.call(this, "Unable to write to file. Server says: " + + apf.webdav.STATUS_CODES[String(iStatus)]); + if (this.dispatchEvent("error", { + error : oError, + bubbles : true + }) === false && !callback) + throw oError; + callback && callback.call(this, data, apf.ERROR, extra); + } + else { + _self.getProperties(sPath, 0, callback); + } + }, sPath, sContent, bLock && oLock.token + ? {"If": "<" + oLock.token + ">"} + : null, null, oBinary); + }; + + /** + * Copies a file or directory resource to any location on the same WebDAV + * server. + * + * @param {String} sFrom Path to the file on the WebDAV server to be copied + * @param {String} sTo New location to place the copy at + * @param {Boolean} [bOverwrite] Tells whether to overwrite any existing resource + * @param {Boolean} [bLock] Whether to require a lock before copy + * @param {Function} callback Function to execute when the request was successful + * @type {void} + */ + this.copy = function(sFrom, sTo, bOverwrite, bLock, callback) { + if (!sTo || sFrom == sTo) + return (callback && callback("", apf.SUCCESS, {})); + + if (bLock) { + var oLock = this.lock(sFrom); + if (!oLock.token) + return updateLockedStack.call(this, oLock, "copy", arguments); + } + + this.method = "COPY"; + var oHeaders = { + "Destination": sTo || this.$server + }; + if (typeof bOverwrite == "undefined") + bOverwrite = true; + if (!bOverwrite) + oHeaders["Overwrite"] = "F"; + if (bLock && oLock.token) + oHeaders["If"] = "<" + oLock.token + ">"; + this.doRequest(function(data, state, extra) { + bLock && unregisterLock.call(this, sFrom); + var iStatus = parseInt(extra.status); + if (iStatus == 400 || iStatus == 403 || iStatus == 409 || iStatus == 412 + || iStatus == 423 || iStatus == 424 || iStatus == 502 + || iStatus == 507) { + var oError = WebDAVError.call(this, "Unable to copy file '" + sFrom + + "' to '" + sTo + "'. Server says: " + + apf.webdav.STATUS_CODES[String(iStatus)]); + if (this.dispatchEvent("error", { + error : oError, + bubbles : true + }) === false && !callback) + throw oError; + callback && callback.call(this, data, state, extra); + } + else { + // nodes needs to be added to the cache, callback passed through + // to notify listener(s) + this.getProperties(sTo, 0, callback); + } + }, sFrom, null, oHeaders); + }; + + /** + * Moves a file or directory resource to any location on the same WebDAV + * server. + * + * @param {String} sFrom Path to the file on the WebDAV server to be moved + * @param {String} sTo New location to move the resource to + * @param {Boolean} [bOverwrite] Tells whether to overwrite any existing resource + * @param {Boolean} [bLock] Whether to require a lock before move + * @param {Function} callback Function to execute when the request was successful + * @type {void} + */ + this.rename = + this.move = function(sFrom, sTo, bOverwrite, bLock, callback) { + if (!sTo || sFrom == sTo) + return (callback && callback("", apf.SUCCESS, {})); + + if (bLock) { + var oLock = this.lock(sFrom); + if (!oLock.token) + return updateLockedStack.call(this, oLock, "move", arguments); + } + + this.method = "MOVE"; + var oHeaders = { + "Destination": sTo || this.$server + }; + if (typeof bOverwrite == "undefined") + bOverwrite = true; + if (!bOverwrite) + oHeaders["Overwrite"] = "F"; + if (bLock && oLock.token) + oHeaders["If"] = "<" + oLock.token + ">"; + this.doRequest(function(data, state, extra) { + bLock && unregisterLock.call(this, sFrom); + var iStatus = parseInt(extra.status); + if (iStatus == 400 || iStatus == 403 || iStatus == 409 || iStatus == 412 + || iStatus == 423 || iStatus == 424 || iStatus == 501 || iStatus == 502 || iStatus == 500) { + var oError = WebDAVError.call(this, "Unable to move file '" + sFrom + + "' to '" + sTo + "'. Server says: " + + apf.webdav.STATUS_CODES[String(iStatus)]); + if (this.dispatchEvent("error", { + error : oError, + bubbles : true + }) === false && !callback) + throw oError; + } + else { //success!! + if (this.$fsCache[sFrom]) { + this.$fsCache[sTo] = this.$fsCache[sFrom]; + this.$fsCache[sTo].path = sTo; + delete this.$fsCache[sFrom]; + } + } + callback && callback.call(this, data, state, extra); + }, sFrom, null, oHeaders); + }; + + /** + * Removes an existing directory or file resource from the WebDAV server. + * + * @param {String} sPath Path to the resource to be removed from the WebDAV server + * @param {Boolean} [bLock] Whether to require a lock before remove + * @param {Function} callback Function to execute when the request was successful + * @type {void} + */ + this.remove = function(sPath, bLock, callback) { + if (bLock) { + var oLock = this.lock(sPath); + if (!oLock.token) + return updateLockedStack.call(this, oLock, "remove", arguments); + } + + this.method = "DELETE"; + this.doRequest(function(data, state, extra) { + bLock && unregisterLock.call(this, sPath); + var iStatus = parseInt(extra.status); + if (iStatus == 400 || iStatus == 423 || iStatus == 424) { //Failed dependency (collections only) + var oError = WebDAVError.call(this, "Unable to remove file '" + sPath + + "'. Server says: " + + apf.webdav.STATUS_CODES[String(iStatus)]); + if (this.dispatchEvent("error", { + error : oError, + bubbles : true + }) === false && !callback) + throw oError; + } + callback && callback.call(this, data, state, extra); + }, sPath, null, bLock && oLock.token + ? {"If": "<" + oLock.token + ">"} + : null); + }; + + this.report = function(sPath, reportName, oProperties, callback) { + var aCont = ['', + '']; + if (oProperties) { + for (var prop in oProperties) { + aCont.push('' + apf.xmlentities(apf.escapeXML(oProperties[prop])) + '' + : '/>')); + } + } + aCont.push(''); + + this.method = "REPORT"; + this.doRequest(function(data, state, extra) { + var iStatus = parseInt(extra.status); + if (state != apf.SUCCESS) { + var oError = WebDAVError.call(this, "Unable to fetch report on '" + sPath + + "'. Server says: " + + apf.webdav.STATUS_CODES[String(iStatus)]); + if (this.dispatchEvent("error", { + error : oError, + bubbles : true + }) === false && !callback) + throw oError; + } + callback && callback.call(this, data, state, extra); + }, sPath, aCont.join("")); + }; + + /** + * Wrapper function that centrally manages locking of resources. Files and + * directories (resources) can be locked prior to any modifying operation to + * prevent the resource being modified by another user before the transaction + * of this user has finished or even started. + * + * @see teleport.webdav.method.unlock + * @param {String} sPath Path to the resource on the server to be locked + * @param {Number} [iDepth] Depth of lock recursion down the tree, should be '1' or 'Infinity' + * @param {Number} [iTimeout] Lifetime of the lock, in seconds. Defaults to Infinite. + * @param {String} [sLock] Previous lock token + * @param {Function} [callback] Function that is executed upon a successful LOCK request + * @type {Object} + */ + this.lock = function(sPath, iDepth, iTimeout, sLock, callback) { + // first, check for existing lock + var oLock = this.$locks[sPath]; + if (oLock && oLock.token) { + //@todo renew the lock (if needed - check timeout)... + return oLock; + } + + this.method = "LOCK" + + iTimeout = iTimeout ? "Infinite, Second-4100000000" : "Second-" + iTimeout; + var oHeaders = { + "Timeout": iTimeout + }; + if (iDepth) + oHeaders["Depth"] = iDepth || "Infinity"; + if (sLock) + oHeaders["If"] = "<" + sLock + ">"; + var xml = '' + + '' + + '' + + '' + + '' + + document.location.toString().escapeHTML() + + + '' + + ''; + this.doRequest(registerLock, sPath, xml, oHeaders, true, null, callback); + return newLock.call(this, sPath); + }; + + /** + * Wrapper function that centrally manages the unlocking of resources that + * have been locked earlier on. + * + * @see teleport.webdav.method.lock + * @param {Object} oLock Object representing a Lock on a resource + * @param {Function} callback Function that is executed upon a successful UNLOCK request + * @type {void} + */ + this.unlock = function(oLock, callback) { + if (typeof oLock == "string") + oLock = this.$locks[oLock]; + if (!oLock || !oLock.token) return; + + this.method = "UNLOCK"; + this.doRequest(function(data, state, extra) { + unregisterLock.call(this, extra.url.replace(this.$server, '')); + }, oLock.path, null, { + "Lock-Token": "<" + oLock.token + ">" + }, true, callback); + }; + + /* + * Add a new lock token/ object to the stack + * + * @path {String} sPath Path pointing to the resource on the server + * @type {Object} + * @private + */ + function newLock(sPath) { + return this.$locks[sPath] = { + path : sPath, + id : this.$lockId++, + token: null + }; + } + + /* + * Handler function that registers a lock globally when a LOCK request was + * successful. It parses all the info it received from the server response + * and caches that info for reuse. + * + * @param {XmlDocument} data Actual XML data, received from the server + * @param {Number} state Internal - APF defined - state of the request + * @param {Object} extra Simple object that contains additional request data + * @type {void} + * @private + */ + function registerLock(data, state, extra) { + var iStatus = parseInt(extra.status), + sPath = extra.url.replace(this.$server, ''), + oLock = this.$locks[sPath] || newLock.call(this, sPath); + if (iStatus == 400 || iStatus == 409 || iStatus == 423 || iStatus == 412) { + // lock failed, so unregister it immediately + unregisterLock.call(this, extra.url.replace(this.$server, '')); + var oError = WebDAVError.call(this, "Unable to apply lock to '" + sPath + + "'. Server says: " + + apf.webdav.STATUS_CODES[String(iStatus)]); + if (this.dispatchEvent("error", { + error : oError, + bubbles : true + }) === false) + throw oError; + } + + var NS = apf.webdav.NS, + oOwner = $xmlns(data, "owner", NS.ns0)[0], + oToken = $xmlns(data, "locktoken", NS.D)[0]; + oLock.path = sPath; + oLock.type = "write"; + oLock.scope = $xmlns(data, "exclusive", NS.D).length ? "exclusive" : "shared"; + oLock.depth = $xmlns(data, "depth", NS.D)[0].firstChild.nodeValue; + oLock.owner = $xmlns(oOwner, "href", NS.ns0)[0].firstChild.nodeValue; + oLock.timeout = $xmlns(data, "timeout", NS.D)[0].firstChild.nodeValue; + oLock.token = $xmlns(oToken, "href", NS.D)[0].firstChild.nodeValue.split(":")[1]; + + purgeLockedStack.call(this, oLock); + } + + /* + * Removes a Lock token/ object from the stack. + * + * @param {String} sPath Path pointing to the resource on the server + * @type {void} + * @private + */ + function unregisterLock(sPath) { + var oLock = this.$locks[sPath]; + if (!oLock) return; + purgeLockedStack.call(this, oLock, true); + this.$locks[sPath] = oLock = null; + delete this.$locks[sPath]; + } + + /* + * Update the stack of lock requests (NOT the stack of valid locks!) with a + * new Lock, or an updated one (lifetime may have changed) + * + * @param {Object} oLock Object that contains specific info about the Lock + * @param {String} sFunc Name of the function that requested the lock + * @param {Array} aArgs List of arguments that should get passed to that function when the lock is available + * @type {Object} + * @private + */ + function updateLockedStack(oLock, sFunc, aArgs) { + return this.$lockedStack.push({ + lockId: oLock.id, + func : sFunc, + args : aArgs + }); + } + + /* + * Purge the stack of lock requests, called when a lock request returned a + * result. If bFailed is set to TRUE, the function that requested the lock + * will be executed. + * + * @param {Object} oLock Simple object that represents a validated Lock + * @param {Boolean} bFailed Tells whether the requesting function may be excuted + * @type {void} + * @private + */ + function purgeLockedStack(oLock, bFailed) { + for (var i = this.$lockedStack.length - 1; i >= 0; i--) { + if (this.$lockedStack[i].lockId != oLock.id) continue; + if (!bFailed) + this[this.$lockedStack[i].func].apply(this, this.$lockedStack[i].args); + this.$lockedStack.remove(i); + } + } + + /** + * Request the server to list the properties of a specific resource and, + * possibly, its children. + * + * @param {String} sPath Path pointing to the resource on the server + * @param {Number} iDepth Depth of lock recursion down the tree, should be '1' or 'Infinity' + * @param {Function} callback Function that is executed upon a successful LOCK request + * @param {Object} oHeaders Additional headers in key: value format + * @type {void} + */ + this.getProperties = function(sPath, iDepth, callback, oHeaders) { + // Note: caching is being done by an external model + this.method = "PROPFIND"; + // XXX maybe we want to change this to allow getting selected props + var xml = '' + + '' + + '' + + ''; + oHeaders = oHeaders || {}; + oHeaders["Depth"] = typeof iDepth != "undefined" ? iDepth : 1 + this.doRequest(parsePropertyPackets, sPath, xml, oHeaders, true, null, callback); + }; + + /** + * Request the server to change and set properties of a specific resource + * with different values. + * @todo Untested functionality + * + * @param {String} sPath Path pointing to the resource on the server + * @param {Object} oPropsSet A mapping from namespace to a mapping of key/value pairs (where value is an *entitized* XML string) + * @param {Object} oPropsDel A mapping from namespace to a list of names + * @param {String} sLock Lock identifier + * @private + */ + this.setProperties = function(sPath, oPropsSet, oPropsDel, sLock) { + this.method = "PROPPATCH"; + + this.doRequest(function(data, state, extra) { + + }, sPath, buildPropertiesBlock.call(this, oPropsSet, oPropsDel), + sLock ? {"If": "<" + sLock + ">"} : null, true); + }; + + /* + * create the XML for a PROPPATCH request. + * + * @param {Object} oPropsSet A mapping from namespace to a mapping of key/value pairs (where value is an *entitized* XML string) + * @param {Object} oPropsDel A mapping from namespace to a list of names + * @type {String} + * @private + */ + function buildPropertiesBlock(oPropsSet, oPropsDel) { + var aOut = ['', + '']; + + var bHasProps = false, ns, i, j; + for (ns in oPropsSet) { + bHasProps = true; + break; + } + if (bHasProps) { + aOut.push(''); + for (ns in oPropsSet) { + for (i in oPropsSet[ns]) + aOut.push('', oPropsSet[ns][i], '') + } + aOut.push(''); + } + bHasProps = false; + for (ns in oPropsDel) { + bHasProps = true; + break; + } + if (bHasProps) { + aOut.push(''); + for (ns in oPropsDel) { + for (i = 0, j = oPropsDel[ns].length; i < j; i++) + aOut.push('<', oPropsDel[ns][i], ' xmlns="', ns, '"/>') + } + aOut.push(''); + } + + aOut.push(''); + return aOut.join(''); + } + + /* + * Handler function that parses the response of a successful PROPFIND + * request. It parses all the info it received from the server response + * and caches that info for reuse. + * + * @param {XmlDocument} data Actual XML data, received from the server + * @param {Number} state Internal - APF defined - state of the request + * @param {Object} extra Simple object that contains additional request data + * @param {Function} callback Function to be executed when all the property packets have been parsed + * @type {void} + * @private + */ + function parsePropertyPackets(oXml, state, extra, callback) { + var status = parseInt(extra.status) + if (status == 403 || status == 401 || !oXml) + return callback ? callback.call(this, null, state, extra) : notAuth.call(this); + + if (typeof oXml == "string") + oXml = apf.getXml(oXml); + + var aResp = $xmlns(oXml, "response", apf.webdav.NS.D), + aOut = []; + if (aResp.length) //we got a valid result set, so assume that any possible AUTH has succeeded + this.$regVar("authenticated", true); + + var sPath; + for (var sa = [], data, i = 0, j = aResp.length; i < j; i++) { + // Exclude requesting URL if it matches node's HREF (same node) + sPath = decodeURIComponent($xmlns(aResp[i], "href", apf.webdav.NS.D)[0].firstChild.nodeValue); + if (sPath === extra.url) + continue; + + parseItem.call(this, aResp[i], data = {}); + if (data.data) + sa.push({ + toString: function(){ + return this.v; + }, + data : data.data, + v : (data.data.type == "file" ? 1 : 0) + "" + data.data.name.toLowerCase() + }); + } + + sa.sort(); + + for (var i = 0, l = sa.length; i < l; i++) { + aOut.push(sa[i].data.xml); + } + +// var start = (extra.headers && typeof extra.headers.Depth != "undefined" && extra.headers.Depth == 0) ? 0 : 1; +// for (var i = start, j = aResp.length; i < j; i++) +// aOut.push(parseItem.call(this, aResp[i])); + + callback && callback.call(this, "" + aOut.join("") + "", state, extra); + } + + /* + * Turn an XML WebDAV node that represents a resource and turn it into a + * reusable JS object, cache it so that it can be reused later. + * + * @param {XmlNode} oNode + * @type {String} + * @private + */ + function parseItem(oNode, extra) { + var NS = apf.webdav.NS, + sPath = decodeURIComponent($xmlns(oNode, "href", NS.D)[0].firstChild + .nodeValue.replace(/[\\\/]+$/, "")), + sName = sPath.split("/").pop(), + bHidden = (sName.charAt(0) == "."); + + if (!this.$showHidden && bHidden) + return ""; + + var t, oItem, + sType = $xmlns(oNode, "collection", NS.D).length > 0 ? "folder" : "file", + aCType = $xmlns(oNode, "getcontenttype", NS.D), + aExec = $xmlns(oNode, "executable", NS.lp2); + oItem = this.$fsCache[sPath] = apf.extend(this.$fsCache[sPath] || {}, { + path : sPath, + type : sType, + size : parseInt(sType == "file" + ? (t = $xmlns(oNode, "getcontentlength", NS.lp1)).length ? t[0].firstChild.nodeValue : 0 + : 0), + name : sName, + contentType : (sType == "file" && aCType.length + ? aCType[0].firstChild.nodeValue + : ""), + creationDate: (t = $xmlns(oNode, "creationdate", NS.lp1)).length ? t[0].firstChild.nodeValue : "", + lastModified: (t = $xmlns(oNode, "getlastmodified", NS.lp1)).length ? t[0].firstChild.nodeValue : "", + etag : (t = $xmlns(oNode, "getetag", NS.lp1)).length ? t[0].firstChild.nodeValue : "", + lockable : ($xmlns(oNode, "locktype", NS.D).length > 0), + executable : (aExec.length > 0 && aExec[0].firstChild.nodeValue == "T") + }); + + if (extra) + extra.data = oItem; + + return oItem.xml = "<" + sType + " path='" + sPath + "' type='" + sType + + "' size='" + oItem.size + "' name='" + oItem.name + "' contenttype='" + + oItem.contentType + "' modifieddate='" + oItem.lastModified + "' creationdate='" + oItem.creationDate + + "' lockable='" + oItem.lockable.toString() + "' hidden='" + + bHidden.toString() + "' executable='" + oItem.executable.toString() + + "'/>"; + } + + + + /** + * Instruction handler for WebDav protocols. + */ + this.exec = function(method, args, callback){ + var cb = function(data, state, extra) { + extra.originalArgs = args + if (typeof args[args.length - 1] == "function") + args[args.length - 1](data, state, extra); + callback && callback(data, state, extra); + }; + // RULE for case aliases: first, topmost match is the preferred term for any + // action and should be used in demos/ examples in + // favor of other aliases. + switch (method) { + case "login": + case "authenticate": + this.authenticate(args[0], args[1], cb); + break; + case "logout": + this.reset(); + break; + case "exists": + this.exists(args[0], cb); + break; + case "read": + this.readFile(args[0], cb); + break; + case "create": + var path = args[0] ? args[0] : ""; + if (path.charAt(path.length - 1) != "/") + path = path + "/"; + this.writeFile(path + args[1], args[2], args[3] || false, cb); + break; + case "write": + case "store": + case "save": + this.writeFile(args[0], args[1], args[2] || false, cb); + break; + case "copy": + case "cp": + this.copy(args[0], args[1], args[2] || true, args[3] || false, cb); + break; + case "rename": + var sBasepath = args[1].substr(0, args[1].lastIndexOf("/") + 1); + this.rename(args[1], sBasepath + args[0], args[2] || false, args[3] || false, cb); + break; + case "move": + case "mv": + path = args[1]; + if (path.charAt(path.length - 1) != "/") + path = path + "/"; + this.rename(args[0], path + args[0].substr(args[0].lastIndexOf("/") + 1), + args[2] || false, args[3] || false, cb); + break; + case "remove": + case "rmdir": + case "rm": + this.remove(args[0], args[1] || false, cb); + break; + case "readdir": + case "scandir": + this.readdir(args[0], cb); + break; + case "getroot": + this.getProperties(this.$rootPath, 0, cb); + break; + case "mkdir": + path = args[0] ? args[0] : ""; + if (path.charAt(path.length - 1) != "/") + path = path + "/"; + this.mkdir(path + args[1], args[2] || false, cb) + break; + case "lock": + this.lock(args[0], null, null, null, cb); + break; + case "unlock": + this.unlock(args[0], cb); + break; + case "report": + this.report(args[0], args[1], args[2], cb); + break; + default: + + break; + } + }; + + +}).call(apf.webdav.prototype = new apf.Teleport()); + +apf.aml.setElement("webdav", apf.webdav); + +// Collection of shorthands for all namespaces known and used by this class +apf.webdav.NS = { + D : "DAV:", + ns0 : "DAV:", + lp1 : "DAV:", + lp2 : "http://apache.org/dav/props/" +}; + +apf.webdav.STATUS_CODES = { + '100': 'Continue', + '101': 'Switching Protocols', + '102': 'Processing', + '200': 'OK', + '201': 'Created', + '202': 'Accepted', + '203': 'None-Authoritive Information', + '204': 'No Content', + '1223': 'No Content', + '205': 'Reset Content', + '206': 'Partial Content', + '207': 'Multi-Status', + '300': 'Multiple Choices', + '301': 'Moved Permanently', + '302': 'Found', + '303': 'See Other', + '304': 'Not Modified', + '305': 'Use Proxy', + '307': 'Redirect', + '400': 'Bad Request', + '401': 'Unauthorized', + '402': 'Payment Required', + '403': 'Forbidden', + '404': 'Not Found', + '405': 'Method Not Allowed', + '406': 'Not Acceptable', + '407': 'Proxy Authentication Required', + '408': 'Request Time-out', + '409': 'Conflict', + '410': 'Gone', + '411': 'Length Required', + '412': 'Precondition Failed', + '413': 'Request Entity Too Large', + '414': 'Request-URI Too Large', + '415': 'Unsupported Media Type', + '416': 'Requested range not satisfiable', + '417': 'Expectation Failed', + '422': 'Unprocessable Entity', + '423': 'Locked', + '424': 'Failed Dependency', + '500': 'Internal Server Error', + '501': 'Not Implemented', + '502': 'Bad Gateway', + '503': 'Service Unavailable', + '504': 'Gateway Time-out', + '505': 'HTTP Version not supported', + '507': 'Insufficient Storage'//, +// 12002 ERROR_INTERNET_TIMEOUT +// 12007 ERROR_INTERNET_NAME_NOT_RESOLVED +// 12029 ERROR_INTERNET_CANNOT_CONNECT +// 12030 ERROR_INTERNET_CONNECTION_ABORTED +// 12031 ERROR_INTERNET_CONNECTION_RESET +// 12152 ERROR_HTTP_INVALID_SERVER_RESPONSE +}; + + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/elements/xmpp.js)SIZE(101265)TIME(Fri, 14 Oct 2011 08:01:44 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/elements/actiontracker/undodata.js)SIZE(11852)TIME(Fri, 14 Oct 2011 08:01:44 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + + +/** + * UndoData is the command object for the actiontracker. Each instance of this class + * contains information about a single event in the application. It can be undone + * and it knows how to synchronize the change to a (remote) data source. + * + * @constructor + * @default_private + */ +apf.UndoData = function(settings, at){ + this.localName = "UndoData"; + this.extra = {}; + + apf.extend(this, settings); + + if (!this.timestamp) + this.timestamp = (new Date()).getUTCTime(); + + if (at) + this.at = at; + //Copy Constructor + else if (settings && settings.tagName == "UndoData") { + this.args = settings.args.slice(); + + } + //Constructor + else { + /* + @todo: Please check the requirement for this and how to solve + this. Goes wrong with multiselected actions! + */ + this.selNode = this.selNode || (this.action == "removeNode" + ? this.args[0] + : (this.amlNode + ? this.amlNode.selected + : null)); + } + + var options, _self = this; + + + + + + /** + * Save the change to a data source. + * @param {Boolean} undo whether the change is undone. + */ + this.saveChange = function(undo, at, callback){ + //Grouped undo/redo support + if (this.action == "group") { + var rpcNodes = this.args[1]; + at.$addToQueue(rpcNodes, undo, true); + return at.$queueNext(this); + } + + var dataInstruction; + if (this.xmlActionNode) { + dataInstruction = this.xmlActionNode.getAttribute(undo ? "undo" : "set"); + if (undo && !dataInstruction) + dataInstruction = this.xmlActionNode.getAttribute("set"); + } + + if (!dataInstruction) { + + return at.$queueNext(this); + } + + this.state = undo ? "restoring" : "saving"; + + if (!options.asyncs) { //Precall didnt contain any async calls + return at.$receive(null, apf.SUCCESS, {amlNode: this.amlNode}, + this, callback); + } + + options.ignoreOffline = true; + apf.saveData(dataInstruction, options); + }; + + this.preparse = function(undo, at, multicall, callback){ + var dataInstruction; + if (this.xmlActionNode) { + dataInstruction = this.xmlActionNode.getAttribute(undo ? "undo" : "set"); + if (undo && !dataInstruction) + dataInstruction = this.xmlActionNode.getAttribute("set"); + } + + if (!dataInstruction) + return this; + + options = apf.extend({ + xmlNode : this.action == "multicall" + ? this.args[0].xmlNode + : this.selNode || this.xmlNode, + userdata : apf.isTrue(this.xmlActionNode.getAttribute("ignore-fail")), + multicall : multicall, + undo : undo, + precall : true, + + //Callback is only for async calls + callback : function(data, state, extra){ + if (options.asyncs) { //Set by apf.saveData + extra.amlNode = _self.amlNode; + return at.$receive(data, state, extra, _self, callback); + } + } + }, this.extra); + + + + apf.saveData(dataInstruction, options); //@todo please check if at the right time selNode is set + + return this; + }; +}; + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/elements/actiontracker/xmlactions.js)SIZE(8814)TIME(Fri, 14 Oct 2011 08:01:44 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + +/** + * Default actions, that are known to the actiontracker + * @todo test if the .extra speed impact matters + * @todo ifdef the undo sections to only be there when the actiontracker is enabled + * @private + */ +apf.actiontracker.actions = { + "setTextNode" : function(undoObj, undo){ + var q = undoObj.args; + + // Set Text Node + if (!undo) + apf.xmldb.setTextNode(q[0], q[1], q[2], undoObj); + else //Undo Text Node Setting + apf.xmldb.setTextNode(q[0], undoObj.extra.oldValue, q[2], undoObj); + }, + + "setAttribute" : function(undoObj, undo){ + var q = undoObj.args; + + // Set Attribute + if (!undo) { + //Set undo info + undoObj.extra.name = q[1]; + undoObj.extra.oldValue = q[0].getAttribute(q[1]); + + apf.xmldb.setAttribute(q[0], q[1], q[2], q[3], undoObj); + } + // Undo Attribute Setting + else { + if (!undoObj.extra.oldValue) + apf.xmldb.removeAttribute(q[0], q[1], null, undoObj); + else + apf.xmldb.setAttribute(q[0], q[1], undoObj.extra.oldValue, q[3], undoObj); + } + }, + + "removeAttribute" : function(undoObj, undo){ + var q = undoObj.args; + + // Remove Attribute + if (!undo) { + // Set undo info + undoObj.extra.name = q[1]; + undoObj.extra.oldValue = q[0].getAttribute(q[1]); + + apf.xmldb.removeAttribute(q[0], q[1], q[2], undoObj); + } + //Undo Attribute Removal + else + apf.xmldb.setAttribute(q[0], q[1], undoObj.extra.oldValue, q[2], undoObj); + }, + + "replaceNode" : function(undoObj, undo){ + var q = undoObj.args; + + //Set Attribute + if (!undo) + apf.xmldb.replaceNode(q[0], q[1], q[2], undoObj); + //Undo Attribute Setting + else + apf.xmldb.replaceNode(q[1], q[0], q[2], undoObj); + }, + + "addChildNode" : function(undoObj, undo){ + var q = undoObj.args; + + //Add Child Node + if (!undo) + apf.xmldb.addChildNode(q[0], q[1], q[2], q[3], undoObj); + //Remove Child Node + else + apf.xmldb.removeNode(undoObj.extra.addedNode, null, undoObj); + }, + + "appendChild" : function(undoObj, undo){ + var q = undoObj.args; + + //Append Child Node + if (!undo) + apf.xmldb.appendChild(q[0], q[1], q[2], q[3], q[4], undoObj); + //Remove Child Node + else + apf.xmldb.removeNode(undoObj.xmlNode, null, undoObj);//q[1] + }, + + "moveNode" : function(undoObj, undo){ + var q = undoObj.args; + + //Move Node + if (!undo) + apf.xmldb.moveNode(q[0], q[1], q[2], q[3], undoObj); + //Move Node to previous position + else + apf.xmldb.moveNode(undoObj.extra.oldParent, q[1], + undoObj.extra.beforeNode, q[3], undoObj); + }, + + "removeNode" : function(undoObj, undo){ + var q = undoObj.args; + + //Remove Node + if (!undo) + apf.xmldb.removeNode(q[0], q[1], undoObj); + //Append Child Node + else + apf.xmldb.appendChild(undoObj.extra.parent, + undoObj.extra.removedNode, undoObj.extra.beforeNode, + null, null, undoObj); + }, + + /** + * @deprecated Use "multicall" from now on + */ + "removeNodeList" : function(undoObj, undo){ + if (undo) { + var d = undoObj.extra.removeList; + for (var i = d.length - 1; i >= 0; i--) { + apf.xmldb.appendChild(d[i].pNode, + d[i].removedNode, d[i].beforeNode, null, null, undoObj); + } + } + else + apf.xmldb.removeNodeList(undoObj.args, undoObj); + }, + + "group" : function(undoObj, undo, at){ + if (!undoObj.$undostack) { + var done = undoObj.args[0]; + undoObj.$undostack = done; + undoObj.$redostack = []; + } + + at[undo ? "undo" : "redo"](undoObj.$undostack.length, false, + undoObj.$undostack, undoObj.$redostack); + }, + + /*"setProperty" : function(undoObj, undo){ + var q = undoObj.args;//amlNode, name, value + + if (!undo) { + undoObj.extra.oldValue = q[0][q[1]]; + q[0].setProperty(q[1], q[2], q[3], q[4]); + } + // Undo + else { + q[0].setProperty(q[1], undoObj.extra.oldValue); + } + },*/ + + "setValueByXpath" : function(undoObj, undo){ + var newNode, q = undoObj.args;//xmlNode, value, xpath + + // Setting NodeValue and creating the node if it doesnt exist + if (!undo) { + if (newNode = undoObj.extra.newNode) { + if (newNode.nodeType == 2) { + apf.xmldb.setAttribute(undoObj.extra.ownerElement, + newNode.nodeName, newNode.nodeValue); + undoObj.extra.newNode = undoObj.extra.ownerElement + .getAttributeNode(newNode.nodeName); + } + else + apf.xmldb.appendChild(undoObj.extra.parentNode, + undoObj.extra.newNode, null, null, null, undoObj); + } + else { + var newNodes = []; + apf.setNodeValue(q[0], q[1], true, { + undoObj : undoObj, + xpath : q[2], + newNodes : newNodes, + forceNew : q[3] + }); + + newNode = undoObj.extra.newNode = newNodes[0]; + if (newNode) { + if (newNode.nodeType == 2) + undoObj.extra.ownerElement = newNode.ownerElement + || newNode.selectSingleNode(".."); + else + undoObj.extra.parentNode = undoObj.extra.newNode.parentNode; + } + } + } + // Undo Setting NodeValue + else { + if (newNode = undoObj.extra.newNode) { + if (newNode.nodeType == 2) + apf.xmldb.removeAttribute(undoObj.extra.ownerElement, + newNode.nodeName, null, undoObj); + else + apf.xmldb.removeNode(undoObj.extra.newNode, null, undoObj); + } + else + apf.setNodeValue(undoObj.extra.appliedNode, + undoObj.extra.oldValue, true, {undoObj: undoObj}); + } + }, + + //@todo please change .func to .action for consistency reasons + "multicall" : function(undoObj, undo, at){ + var q = undoObj.args; + + var dUpdate = apf.xmldb.delayUpdate; + apf.xmldb.delayUpdate = true; + + // Set Calls + if (!undo) { + for(var i = 0; i < q.length; i++) { + if (!q[i].extra) + q[i].extra = {}; + + apf.actiontracker.actions[q[i].action](q[i], false, at); + } + + } + // Undo Calls + else { + for (var i = q.length - 1; i >= 0; i--) + apf.actiontracker.actions[q[i].action](q[i], true, at); + } + + apf.xmldb.delayUpdate = dUpdate; + + //if (!dUpdate) + //apf.xmldb.notifyQueued(); + } +}; + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/elements/audio/type_flash.js)SIZE(12951)TIME(Fri, 14 Oct 2011 08:01:44 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/elements/audio/type_native.js)SIZE(11013)TIME(Fri, 14 Oct 2011 08:01:44 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/elements/modalwindow/widget.js)SIZE(7077)TIME(Fri, 14 Oct 2011 08:01:44 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/elements/rpc/cgi.js)SIZE(7168)TIME(Fri, 14 Oct 2011 08:01:44 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + + +/** + * Implementation of the Common Gateway Interface (CGI) as a module for the RPC + * plugin of apf.teleport. + * Example: + * Ajax.org Markup Language + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * //This function is called when the search returns + * function processSearch(data, state, extra){ + * alert(data) + * } + * + * //Execute a search for the product car + * comm.searchProduct('car', 10); + * + * + * Remarks: + * Calls can be made to a server using cgi variables with a special + * {@link term.datainstruction data instruction} + * format. + * + * get="http://www.bla.nl?blah=10&foo=[@bar]&example=[10+5]" + * set="post http://www.bla.nl?blah=10&foo={/bar}&example=[10+5]" + * + * + * @addenum rpc[@protocol]:cgi + * + * @constructor + * + * @inherits apf.Teleport + * @inherits apf.http + * @inherits apf.rpc + * + * @author Ruben Daniels (ruben AT ajax DOT org) + * @version %I%, %G% + * @since 0.4 + * + * @default_private + */ +apf.cgi = function(){ + this.supportMulticall = false; + this.namedArguments = true; + + this.unserialize = function(str){ + return str; + }; + + this.getSingleCall = function(name, args, obj){ + obj.push(args); + }; + + // Create message to send + this.createMessage = function(functionName, args){ + var prop, + vars = []; + + function recur(o, stack){ + if (o && o.dataType == apf.ARRAY) { + for (var j = 0; j < o.length; j++) + recur(o[j], stack + "%5B" + j + "%5D");//" + j + " + } + else if (o && typeof o == "object") { + if (o.nodeType) { + try{ + var s = o.outerHTML || o.serialize && o.serialize() + || apf.getCleanCopy(o).xml; + } + catch(e){ + var s = "Could not serialize object"; + } + vars.push(stack + "=" + encodeURIComponent(s)); + } + else { + for (prop in o) { + + + if (typeof o[prop] == "function") + continue; + recur(o[prop], stack + "%5B" + encodeURIComponent(prop) + "%5D"); + } + } + } + else { + if (typeof o != "undefined" && o !== null) + vars.push(stack + "=" + encodeURIComponent(o)); + } + }; + + if (this.multicall) { + vars.push("func" + "=" + this.mcallname); + for (var i = 0; i < args[0].length; i++) + recur(args[0][i], "f%5B" + i + "%5D"); + } + else { + for (prop in args) { + + + recur(args[prop], prop); + } + } + + if (!this.baseUrl) + this.baseUrl = this.url; + + this.url = this.$methods[functionName].url + ? this.$methods[functionName].url + : this.baseUrl; + + if (this.method != "GET") + return vars.join("&"); + + this.url = this.url + (vars.length + ? (this.url.indexOf("?") > -1 ? "&" : "?") + vars.join("&") + : ""); + + return ""; + }; + + this.addEventListener("DOMNodeInsertedIntoDocument", function() { + this.method = (this["http-method"] || "GET").toUpperCase(); + this.contentType = this.method == "GET" + ? null + : "application/x-www-form-urlencoded"; + }); + + /** + * Submit a form with ajax (GET) + * + * @param {HTMLElement} form the html form element to submit. + * @param {Function} callback called when the http call returns. + */ + this.submitForm = function(form, callback){ + if (!this['postform']) + this.addMethod('postform', callback); + + var args = []; + for (var i = 0, l = form.elements.length; i < l; i++) { + if (!form.elements[i].name) + continue; + if (form.elements[i].tagname == "input" + && (form.elements[i].type == "checkbox" + || form.elements[i].type == "radio") + && !form.elements[i].checked) + continue; + + if (form.elements[i].tagname = "select" && form.elements[i].multiple) { + for (var j = 0; j < form.elements[i].options.length; j++) { + if (form.elements[i].options[j].selected) + args.push(form.elements[i].name + + "=" + + encodeURIComponent(form.elements[i].options[j].value)); + } + } + else { + args.push(form.elements[i].name + + "=" + + encodeURIComponent(form.elements[i].value)); + } + } + + var loc = (form.action || location.href); + this.urls['postform'] = loc + (loc.indexOf("?") > -1 ? "&" : "?") + args.join("&"); + this['postform'].call(this); + + return false; + }; +}; + + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/elements/rpc/header.js)SIZE(3062)TIME(Fri, 14 Oct 2011 08:01:44 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/elements/rpc/jphp.js)SIZE(5874)TIME(Fri, 14 Oct 2011 08:01:44 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/elements/rpc/jsonrpc.js)SIZE(3125)TIME(Fri, 14 Oct 2011 08:01:44 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/elements/rpc/rdb.js)SIZE(8293)TIME(Fri, 14 Oct 2011 08:01:44 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/elements/rpc/rest.js)SIZE(3962)TIME(Fri, 14 Oct 2011 08:01:44 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + + +/** + * Implementation of the Common Gateway Interface (REST) as a module for the RPC + * plugin of apf.teleport. + * Example: + * Ajax.org Markup Language + * + * + * + * + * + * + * + * //This function is called when the search returns + * function processSearch(data, state, extra){ + * alert(data) + * } + * + * //Execute a search for the product car + * comm.searchProduct('car', 10); + * + * + * Remarks: + * Calls can be made to a server using rest variables with a special + * {@link term.datainstruction data instruction} + * format. + * + * get="http://www.bla.nl?blah=10&foo=[@bar]&example=[10+5]" + * set="post http://www.bla.nl?blah=10&foo={/bar}&example=[10+5]" + * + * + * @addenum rpc[@protocol]:rest + * + * @constructor + * + * @inherits apf.Teleport + * @inherits apf.http + * @inherits apf.rpc + * + * @author Ruben Daniels (ruben AT ajax DOT org) + * @author Mike de Boer (mike AT javeline DOT com) + * @version %I%, %G% + * @since 0.4 + * + * @default_private + */ +apf.rest = function(){ + this.supportMulticall = false; + this.namedArguments = false; + this.nocache = false; + + this.unserialize = function(str){ + return str; + }; + + this.getSingleCall = function(name, args, obj){ + obj.push(args); + }; + + // Create message to send + this.createMessage = function(functionName, args){ + if (!this.baseUrl) + this.baseUrl = this.url; + + var options = this.$methods[functionName]; + + + + var body = "NOTIFY|SEND|POST|PUT".indexOf(options["http-method"].toUpperCase()) > -1 ? args.pop() : "", + url; + + this.method = options["http-method"]; + if (options["content-type"]) + this.contentType = options["content-type"]; + + var i = 0, + l = args.length; + for (; i < l; ++i) + args[i] = encodeURIComponent(args[i]); + + this.url = (url = options.url + ? options.url + : this.baseUrl) + (l + ? (url.charAt(url.length - 1) == "/" + ? "" + : "/") + args.join("/") + : ""); + + this.contentType = body ? "application/x-www-form-urlencoded" : null; + return body; + }; +}; + + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/elements/rpc/soap.js)SIZE(10943)TIME(Fri, 14 Oct 2011 08:01:44 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/elements/rpc/xmlrpc.js)SIZE(11154)TIME(Fri, 14 Oct 2011 08:01:44 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/elements/rpc/yql.js)SIZE(3962)TIME(Fri, 14 Oct 2011 08:01:44 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/elements/textbox/autocomplete.js)SIZE(7030)TIME(Fri, 14 Oct 2011 08:01:44 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/elements/textbox/autocomplete2.js)SIZE(14483)TIME(Fri, 14 Oct 2011 08:01:44 GMT)*/ + + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/elements/textbox/masking.js)SIZE(13243)TIME(Fri, 14 Oct 2011 08:01:44 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + + +/** + * @constructor + * @private + */ +apf.textbox.masking = function(){ + /* + Special Masking Values: + - PASSWORD + + + */ + + var _FALSE_ = 9128748732; + + var _REF = { + "0" : "\\d", + "1" : "[12]", + "9" : "[\\d ]", + "#" : "[\\d +-]", + "L" : "[A-Za-z]", + "?" : "[A-Za-z ]", + "A" : "[A-Za-z0-9]", + "a" : "[A-Za-z0-9 ]", + "X" : "[0-9A-Fa-f]", + "x" : "[0-9A-Fa-f ]", + "&" : "[^\s]", + "C" : "." + }; + + var lastPos = -1; + var masking = false; + var oExt = this.$ext + var initial, pos = [], myvalue, format, fcase, replaceChar; + + this.setPosition = function(setpos){ + setPosition(setpos || lastPos || 0); + }; + + this.addEventListener("$clear", function(){ + this.value = ""; + if (this.mask) + return this.setValue(""); + }); + + this.$propHandlers["value"] = function(value){ + var data = ""; + if (this.includeNonTypedChars) { + for (var i = 0; i < initial.length; i++) { + if (initial.substr(i, 1) != value.substr(i, 1)) + data += value.substr(i, 1);//initial.substr(i,1) == replaceChar + } + } + + this.$insertData(data || value); + }; + + //Char conversion + var numpadKeys = { + "96": "0", + "97": "1", + "98": "2", + "99": "3", + "100": "4", + "101": "5", + "102": "6", + "103": "7", + "104": "8", + "105": "9", + "106": "*", + "107": "+", + "109": "-", + "110": ".", + "110": "/" + } + + this.addEventListener("keydown", function(e){ + var key = e.keyCode, + stop = false; + + switch (key) { + case 39: + //RIGHT + setPosition(lastPos + 1); + stop = true; + break; + case 37: + //LEFT + setPosition(lastPos - 1); + stop = true; + break; + case 35: + case 34: + setPosition(myvalue.length); + stop = true; + break; + case 33: + case 36: + setPosition(0); + stop = true; + break; + case 8: + //BACKSPACE + deletePosition(lastPos - 1); + setPosition(lastPos - 1); + stop = true; + break; + case 46: + //DEL + deletePosition(lastPos); + setPosition(lastPos); + stop = true; + break; + default: + if (key == 67 && e.ctrlKey) { + window.clipboardData.setData("Text", this.getValue()); + stop = true; + } + /* + else if ((key == 86 && ctrlKey) || (shiftKey && key == 45)) { + this.setValue(window.clipboardData.getData("Text")); + setPosition(lastPos); + } + else + return;*/ + break; + } + + //@todo why isnt the conversion not always good? Check backtick. + var chr = numpadKeys[key] || String.fromCharCode(key); + if (setCharacter(chr)) + setPosition(lastPos + 1); + + var value, pos = lastPos; + if (this.realtime && (value = this.getValue()) != this.value) { + this.change(value); + setPosition(pos); + } + + if (apf.isCharacter(e.keyCode) || stop) + return false; + }, true); + + /* *********************** + Init + ************************/ + + this.$initMasking = function(){ + ///this.keyHandler = this._keyHandler; + this.$keyHandler = null; //temp solution + masking = true; + + this.$ext[apf.isIphone ? "onclick" : "onmouseup"] = function(e){ + var pos = Math.min(calcPosFromCursor(), myvalue.length); + setPosition(pos); + return false; + }; + + this.$ext.onpaste = function(e){ + e = e || window.event; + e.returnValue = false; + this.host.setValue(window.clipboardData.getData("Text") || ""); + //setPosition(lastPos); + $setTimeout(function(){ + setPosition(lastPos); + }, 1); //HACK good enough for now... + }; + + this.getValue = function(){ + if (this.includeNonTypedChars) + return initial == this.$ext.value + ? "" + : this.$ext.value.replace(new RegExp(replaceChar, "g"), ""); + else + return myvalue.join(""); + }; + + this.setValue = function(value){ + if (this.includeNonTypedChars) { + for (var data = "", i = 0; i < initial.length; i++) { + if (initial.substr(i,1) != value.substr(i,1)) + data += value.substr(i, 1);//initial.substr(i,1) == replaceChar + } + } + this.$insertData(data); + }; + }; + + this.setMask = function(m){ + if (!masking) + this.$initMasking(); + + m = m.split(";"); + replaceChar = m.pop(); + this.includeNonTypedChars = parseInt(m.pop()) !== 0; + var mask = m.join(""); //why a join here??? + var validation = "", + visual = "", + mode_case = "-", + strmode = false, + startRight = false, + chr; + pos = [], format = "", fcase = ""; + + for (var looppos = -1, i = 0; i < mask.length; i++) { + chr = mask.substr(i,1); + + if (!chr.match(/[\!\'\"\>\<\\]/)) + looppos++; + else { + if (chr == "!") + startRight = true; + else if (chr == "<" || chr == ">") + mode_case = chr; + else if (chr == "'" || chr == "\"") + strmode = !strmode; + continue; + } + + if (!strmode && _REF[chr]) { + pos.push(looppos); + visual += replaceChar; + format += chr; + fcase += mode_case; + validation += _REF[chr]; + } + else + visual += chr; + } + + this.$ext.value = visual; + initial = visual; + //pos = pos; + myvalue = []; + //format = format; + //fcase = fcase; + replaceChar = replaceChar; + + //setPosition(0);//startRight ? pos.length-1 : 0); + + //validation.. + //forgot \ escaping... + }; + + function checkChar(chr, p){ + var f = format.substr(p, 1); + var c = fcase.substr(p, 1); + + if (chr.match(new RegExp(_REF[f])) == null) + return _FALSE_; + if (c == ">") + return chr.toUpperCase(); + if (c == "<") + return chr.toLowerCase(); + return chr; + } + + function setPosition(p){ + if (p < 0) + p = 0; + + if (apf.hasMsRangeObject) { + var range = oExt.createTextRange(); + range.expand("textedit"); + range.select(); + + if (pos[p] == null) { + range.collapse(false); + range.select(); + lastPos = pos.length; + return false; + } + + range.collapse(); + range.moveStart("character", pos[p]); + range.moveEnd("character", 1); + range.select(); + } + else { + if (typeof pos[p] == "undefined") { + oExt.selectionStart = oExt.selectionEnd = pos[pos.length - 1] + 1; + lastPos = pos.length; + return false; + } + oExt.selectionStart = pos[p]; + oExt.selectionEnd = pos[p] + 1; + } + + lastPos = p; + } + + function setCharacter(chr){ + if (pos.length && pos[lastPos] == null) return false; + + chr = checkChar(chr, lastPos); + if (chr == _FALSE_) return false; + + if (apf.hasMsRangeObject) { + var range = oExt.createTextRange(); + range.expand("textedit"); + range.collapse(); + range.moveStart("character", pos[lastPos]); + range.moveEnd("character", 1); + range.text = chr; + if (apf.document.activeElement == this) + range.select(); + } + else { + var val = oExt.value, + start = oExt.selectionStart, + end = oExt.selectionEnd; + oExt.value = val.substr(0, start) + chr + val.substr(end); + oExt.selectionStart = start; + oExt.selectionEnd = end; + } + + myvalue[lastPos] = chr; + + return true; + } + + function deletePosition(p){ + if(pos[p] == null) return false; + + if (apf.hasMsRangeObject) { + var range = oExt.createTextRange(); + range.expand("textedit"); + range.collapse(); + + range.moveStart("character", pos[p]); + range.moveEnd("character", 1); + range.text = replaceChar; + range.select(); + } + else { + var val = oExt.value, + start = pos[p], + end = pos[p] + 1; + oExt.value = val.substr(0, start) + replaceChar + val.substr(end); + oExt.selectionStart = start; + oExt.selectionEnd = end; + } + + //ipv lastPos + myvalue[p] = " "; + } + + this.$insertData = function(str){ + if (str == this.getValue()) return; + + var i, j; + + try { + if (!apf.hasMsRangeObject && oExt.selectionStart == oExt.selectionEnd) { + setPosition(0); // is this always correct? practice will show... + } + } + catch (ex) { + // in FF (as we know it), we cannot access the selectStart property + // when the control/ input doesn't have the focus or is not visible. + // A workaround is provided here... + if (!str) return; + var chr, val; + for (i = 0, j = str.length; i < j; i++) { + lastPos = i; + if (pos[lastPos] == null) continue; + chr = checkChar(str.substr(i, 1), i); + if (chr == _FALSE_) continue; + val = oExt.value; + oExt.value = val.substr(0, pos[i]) + chr + val.substr(pos[i] + 1); + } + if (str.length) + lastPos++; + return; // job done, bail out + } + + str = this.dispatchEvent("insert", { data : str }) || str; + + if (!str) { + if (!this.getValue()) return; //maybe not so good fix... might still flicker when content is cleared + for (i = this.getValue().length - 1; i >= 0; i--) + deletePosition(i); + setPosition(0); + return; + } + + for (i = 0, j = str.length; i < j; i++) { + lastPos = i; + setCharacter(str.substr(i, 1)); + if (!apf.hasMsRangeObject) + setPosition(i + 1); + } + if (str.length) + lastPos++; + }; + + function calcPosFromCursor(){ + var range, lt = 0; + + if (!apf.hasMsRangeObject) { + lt = oExt.selectionStart; + } + else { + range = document.selection.createRange(); + var r2 = range.duplicate(); + r2.expand("textedit"); + r2.setEndPoint("EndToStart", range); + lt = r2.text.length; + } + + for (var i = 0; i < pos.length; i++) { + if (pos[i] > lt) + return (i == 0) ? 0 : i - 1; + } + + return myvalue.length; // always return -a- value... + } +}; + + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/elements/upload/flash.js)SIZE(9564)TIME(Fri, 14 Oct 2011 08:01:44 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/elements/upload/html4.js)SIZE(9512)TIME(Fri, 14 Oct 2011 08:01:44 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/elements/upload/html5.js)SIZE(8910)TIME(Fri, 14 Oct 2011 08:01:44 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/elements/video/type_flv.js)SIZE(17057)TIME(Fri, 14 Oct 2011 08:01:44 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/elements/video/type_native.js)SIZE(10825)TIME(Fri, 14 Oct 2011 08:01:44 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/elements/video/type_qt.js)SIZE(23357)TIME(Fri, 14 Oct 2011 08:01:44 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/elements/video/type_silverlight.js)SIZE(15347)TIME(Fri, 14 Oct 2011 08:01:44 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/elements/video/type_vlc.js)SIZE(12493)TIME(Fri, 14 Oct 2011 08:01:44 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/elements/video/type_wmp.js)SIZE(12632)TIME(Fri, 14 Oct 2011 08:01:44 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/elements/xmpp/muc.js)SIZE(18991)TIME(Fri, 14 Oct 2011 08:01:44 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/elements/xmpp/rdb.js)SIZE(21319)TIME(Fri, 14 Oct 2011 08:01:44 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/elements/xmpp/roster.js)SIZE(13725)TIME(Fri, 14 Oct 2011 08:01:44 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/processinginstructions/livemarkup.js)SIZE(4492)TIME(Fri, 14 Oct 2011 08:01:44 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + +/** + * Live Markup processor for a processing instruction + * + * @author Ruben Daniels (ruben AT ajax DOT org) + * @version %I%, %G% + * @since 0.9 + */ + +apf.LiveMarkupPi = function(){ + //this.$data; + this.$init(); +}; + +/* + @todo optimize the pi, possible with this code: + var div, doc = this.ownerDocument, domParser = doc.$domParser, + docFrag = doc.createDocumentFragment(), + sStart = "", + sEnd = ""; + + docFrag.$int = div; + + domParser.parseFromXml(apf.getXml(sStart + this.$bindingQueue[i] + sEnd), { //@todo might be optimized by doing it only once + doc : doc, + amlNode : docFrag, + beforeNode : null, + include : true + }); +*/ +(function(){ + this.mainBind = "data"; + + this.implement(apf.StandardBinding); + + this.getDocument = function(){ + return this.$data && this.$data.ownerDocument; + }; + + this.clear = function(msg){ + if (msg == "loading" && apf.getInheritedAttribute(this, "loading-message")) { + this.$propHandlers["calcdata"].call(this, "Loading..."); + this.calcdata = ""; + } + }; + + this.$propHandlers["calcdata"] = function(data){ + if (this.$skipChange) //Used by liveedit.js + return; + + if (this.$data) { + + if (this.$useXmlDiff) { + var newXml = apf.getXml("" + apf.xmlentities(data) + "", + null, + this.ownerDocument.$domParser.preserveWhiteSpace); //@todo apf3.0 slow, rethink xmlentities + + var oldXml = this.$data; + apf.xmlDiff(oldXml, newXml); + + return; + } + + + var nodes = this.$data.childNodes; + for (var i = nodes.length - 1; i >= 0; i--) + nodes[i].destroy(true); + } + + //var dt = new Date().getTime(); + + //if (!this.xmlRoot) + //return this.$ext.innerHTML = "loading..."; + + if (typeof data == "string" && data.indexOf(" -1) { //@todo use the .hasAml attribute + this.$ext.innerHTML = "";//data; + + var doc = this.ownerDocument.$domParser.parseFromString("" + data + "", "text/xml", { + htmlNode : this.$ext, + host : this + //nodelay : true + }) + this.$data = doc.documentElement; + + //apf.queue.empty(); + + //alert(new Date().getTime() - dt); + } + else { + if (this.$data) { + var nodes = this.$data.childNodes; + for (var i = 0; i < nodes.length; i++) + nodes[i].destroy(true); + } + if (this.$ext) + this.$ext.innerHTML = data || ""; + } + }; +}).call(apf.LiveMarkupPi.prototype = new apf.AmlProcessingInstruction(true)); + +apf.aml.setProcessingInstruction("lm", apf.LiveMarkupPi); +apf.aml.setProcessingInstruction("lm-debug", apf.LiveMarkupPi); +apf.aml.setProcessingInstruction("livemarkup", apf.LiveMarkupPi); + + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/jpack_end.js)SIZE(773)TIME(Mon, 19 Dec 2011 11:04:45 GMT)*/ + + + +//Conditional compilation workaround... (can this be improved??) +if (document.all) { + var oldWinError = window.onerror, z; + window.onerror = z = function(m){ + apf.console.error("Error caught from early startup. Might be a html parser syntax error (not your fault). " + m); + + if (!arguments.caller) + return true; + } +} +apf.Init.addConditional(function(){ + if (document.all && window.onerror == z) //Conditional compilation workaround... (can this be improved??) + window.onerror = oldWinError; + + apf.dispatchEvent("domready"); +}, null, ["body", "class"]); + +/*if(document.body) + apf.Init.run("body"); +else*/ + apf.addDomLoadEvent(function(){apf.Init.run('body');}); + +//Start +apf.start(); + + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/apf-node.js)SIZE(1241)TIME(Fri, 14 Oct 2011 08:01:44 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/apf-o3.js)SIZE(14014)TIME(Fri, 14 Oct 2011 08:01:44 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/loader-o3.js)SIZE(7470)TIME(Fri, 14 Oct 2011 08:01:44 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/loader.js)SIZE(15799)TIME(Wed, 30 Nov 2011 17:29:20 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + +/** + * Bootloader for Ajax.org Platform + * + * Include apf.js, then just go about it as you would with the + * packaged version. Adapt this file to include your preferred modules + */ + + + + +/*FILEHEAD(/Users/mike/Projects/cloud9infra/support/cloud9/support/packager/lib/../support/apf/loader2.js)SIZE(18652)TIME(Fri, 14 Oct 2011 08:01:44 GMT)*/ + +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + +/** + * Bootloader for Ajax.org Platform + * + * Include apf.js, then just go about it as you would with the + * packaged version. Adapt this file to include your preferred modules + */ + + diff --git a/support/apf b/support/apf index 1f3030695d0..8dccf9a9647 160000 --- a/support/apf +++ b/support/apf @@ -1 +1 @@ -Subproject commit 1f3030695d05bd5418a9368d0c5b9eb4cfdb2b0e +Subproject commit 8dccf9a9647928f33adb279fa61f3b8325cc18a9 From fa62f991a5ea86616ce0dc78f14c36bcd0b9dbd2 Mon Sep 17 00:00:00 2001 From: mikedeboer Date: Mon, 19 Dec 2011 12:32:47 +0100 Subject: [PATCH 12/69] fixed splitview grid margins. fixes #400 --- client/ext/splitview/grids.js | 2 +- client/ext/splitview/splitview.js | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/client/ext/splitview/grids.js b/client/ext/splitview/grids.js index dcf192e30b2..7978ccd70ba 100644 --- a/client/ext/splitview/grids.js +++ b/client/ext/splitview/grids.js @@ -211,7 +211,7 @@ function createNodes(struct, splitters, parent) { if ("vbox|hbox".indexOf(nodeName) > -1) { if (parent === apf.document.body) { options.visible = false; - options.anchors = "0 0 0 0"; + options.anchors = "2 0 0 0"; } else { options.flex = 1; diff --git a/client/ext/splitview/splitview.js b/client/ext/splitview/splitview.js index 91ab05fe020..c7c4d6599fd 100644 --- a/client/ext/splitview/splitview.js +++ b/client/ext/splitview/splitview.js @@ -97,10 +97,10 @@ module.exports = ext.register("ext/splitview/splitview", { tabEditors.addEventListener("tabselectmouseup", function(e) { var page = this.$activepage; - var split = Splits.get(page); + var splits = Splits.get(page); - if (split && split.length) - Splits.update(split[0]); + if (splits.length) + Splits.update(splits[0]); }); ide.addEventListener("loadsettings", function(e) { From a6c41a1d356a06be08a308147ad425ce98fd7a8d Mon Sep 17 00:00:00 2001 From: mikedeboer Date: Mon, 19 Dec 2011 15:36:42 +0100 Subject: [PATCH 13/69] improved splitview behavior, speed and navigation. Mostly fixes #401 --- client/ext/splitview/grids.js | 13 +-- client/ext/splitview/splits.js | 30 ++----- client/ext/splitview/splitview.js | 139 +++++++++++++----------------- 3 files changed, 72 insertions(+), 110 deletions(-) diff --git a/client/ext/splitview/grids.js b/client/ext/splitview/grids.js index 7978ccd70ba..e30a2fc79a4 100644 --- a/client/ext/splitview/grids.js +++ b/client/ext/splitview/grids.js @@ -179,14 +179,10 @@ exports.show = function(gridLayout) { if (!grid.node) return; - if (name == gridLayout) { + if (name == gridLayout) grid.node.show(); - } - else { - grid.node.hide(); - if (grid.node.parentNode != apf.document.body) - apf.document.body.appendChild(grid.node); - } + else + grid.node.removeNode(); }); }; @@ -196,8 +192,7 @@ exports.hide = function(gridLayout) { if (!grid || !grid.node) return; - grid.node.hide(); - apf.document.body.appendChild(grid.node); + grid.node.removeNode(); }; function createNodes(struct, splitters, parent) { diff --git a/client/ext/splitview/splits.js b/client/ext/splitview/splits.js index f92a8ef6c69..b36e0e3bbd6 100644 --- a/client/ext/splitview/splits.js +++ b/client/ext/splitview/splits.js @@ -86,7 +86,7 @@ exports.create = function(page, gridLayout) { var editor = page.$editor.amlEditor; editor.setAttribute("model", page.$model); editor.setAttribute("actiontracker", page.$at); - consolidateEditorSession(page, editor); + SplitView.consolidateEditorSession(page, editor); var split = { editors: [editor], @@ -103,7 +103,7 @@ exports.show = function(split) { return this; this.update(split); - if (ActiveSplit) + if (ActiveSplit && ActiveSplit.gridLayout != split.gridLayout) this.hide(ActiveSplit); Grids.show(split.gridLayout); @@ -116,10 +116,11 @@ exports.show = function(split) { aSplit.pages[i].$deactivateButton(); }); //console.log("pages",split.pages.map(function(page){return page.name;})); - for (i = 0, l = split.pages.length; i < l; ++i) + for (i = 0, l = split.pages.length; i < l; ++i) { split.pages[i].$activateButton(); - for (i = 0, l = split.editors.length; i < l; ++i) split.editors[i].show(); + SplitView.consolidateEditorSession(split.pages[i], split.editors[i]); + } ActiveSplit = split; @@ -145,6 +146,8 @@ exports.hide = function(split) { exports.update = function(split, gridLayout) { split = split || ActiveSplit; + if (!split) + return; gridLayout = Grids.init(gridLayout || split.gridLayout); var page = split.pages[0]; @@ -260,9 +263,7 @@ exports.mutate = function(split, page) { split.pages.push(page); split.editors.push(editorToUse); //console.log("setting model of ", editorToUse.id, "to", page.$model.data.xml); - editorToUse.setAttribute("model", page.$model); - editorToUse.setAttribute("actiontracker", page.$at); - consolidateEditorSession(page, editorToUse); + SplitView.consolidateEditorSession(page, editorToUse); this.show(split); } @@ -406,21 +407,8 @@ function onEditorFocus(editor) { }); } -function consolidateEditorSession(page, editor) { - var session = SplitView.getEditorSession(page); - if (!session && page.$editor.setDocument) { - var defEditor = page.$editor.amlEditor; - var oldVal = defEditor.value; - page.$editor.setDocument(page.$doc, page.$at); - session = SplitView.getEditorSession(page); - defEditor.setProperty("value", oldVal); - } - if (editor.value !== session) - editor.setProperty("value", session); -} - function clearSplitViewStyles(splitOrPage) { - var pages = (typeof splitOrPage.tagName != "undefined") ? [splitOrPage] : split.pages; + var pages = (typeof splitOrPage.tagName != "undefined") ? [splitOrPage] : splitOrPage.pages; pages.forEach(function(page) { apf.setStyleClass(page.$button, null, [ActiveClass, InactiveClass]); }); diff --git a/client/ext/splitview/splitview.js b/client/ext/splitview/splitview.js index c7c4d6599fd..94cbd538908 100644 --- a/client/ext/splitview/splitview.js +++ b/client/ext/splitview/splitview.js @@ -97,10 +97,9 @@ module.exports = ext.register("ext/splitview/splitview", { tabEditors.addEventListener("tabselectmouseup", function(e) { var page = this.$activepage; - var splits = Splits.get(page); - - if (splits.length) - Splits.update(splits[0]); + var split = Splits.get(page)[0]; + if (split) + Splits.update(split); }); ide.addEventListener("loadsettings", function(e) { @@ -172,8 +171,7 @@ module.exports = ext.register("ext/splitview/splitview", { var activePage = tabEditors.getPage(); var shiftKey = e.htmlEvent.shiftKey; var ret = null; - var split = Splits.get(activePage); - split = split.length ? split[0] : null; + var split = Splits.get(activePage)[0]; if (split && !shiftKey) { for (var i = 0, l = split.pages.length; i < l; ++i) { @@ -215,71 +213,41 @@ module.exports = ext.register("ext/splitview/splitview", { * @param {AmlEvent} e */ onCycleTab: function(e) { - var idx = e.index; - var dir = e.dir; - var pages = e.pages; - - var split = Splits.get(pages[idx]); - //console.log("cycletab split?", split, pages[idx], e); - if (!split.length) + var pages = e.pages; + var split = Splits.getActive(); + if (!split) return; - split = split[0]; - - var start = split.pages.indexOf(pages[idx]); - - function correct(val) { - if (val < 0) - return pages.length - 1; - if (val > pages.length -1) - return 0; - return val; - } - - //console.log("tab is cycling...", dir); - var count = 0; - var max = pages.length + 2; - if (dir == "right") { - if (start === 0) - return; - while (split.pages[++start] === pages[++idx]) { - if (++count > max) - return; - idx = correct(idx); - } - } - else { - if (start === split.pages.length - 1) - return; - while (split.pages[--start] === pages[--idx]) { - if (++count > max) - return; - idx = correct(idx); - } - } - e.returnValue = correct(idx); + if (split.pages.length == pages.length) + return false; + + var maxIdx = pages.length - 1; + var bRight = e.dir == "right"; + var idx = pages.indexOf(split.pages[bRight ? split.pages.length - 1 : 0]) + (bRight ? 1 : -1); + idx = idx < 0 ? maxIdx : idx > maxIdx ? 0 : idx; + if (split.pages.indexOf(pages[idx]) > -1) + return false; + + // check if the next tab is inside a split as well: + split = Splits.get(pages[idx])[0]; + if (split) + e.returnValue = pages.indexOf(split.pages[0]); + else + e.returnValue = idx; }, updateSplitView: function(previous, next) { + var editor; var doc = next.$doc; var at = next.$at; // check if this is a valid clone session - var split = Splits.get(next); - split = split.length ? split[0] : null; - //console.log("got split?",split); + var split = Splits.get(next)[0]; // hide the previous split view if (previous && previous.$model) { - var oldSplit = Splits.get(previous); - oldSplit = oldSplit.length ? oldSplit[0] : null; + var oldSplit = Splits.get(previous)[0]; //console.log("got old split?",oldSplit); - if (oldSplit && !split) { + if (oldSplit && (!split || oldSplit.gridLayout != split.gridLayout)) Splits.hide(oldSplit); - - // make sure that the editor of the next page is in it's expected - // position - var nextPage = next.fake ? next.relPage : next; - nextPage.appendChild(next.$editor.amlEditor); - } } // enable split view ONLY for code editors for now... @@ -297,27 +265,26 @@ module.exports = ext.register("ext/splitview/splitview", { // all this must exist if (!doc || !at || !split) { - next.$editor.amlEditor && next.$editor.amlEditor.show(); + // if it doesn't, make sure the editor is visible and correctly displayed + editor = next.$editor.amlEditor; + if (!editor) + return; + this.consolidateEditorSession(next, editor); + var nextPage = next.fake ? next.relPage : next; + if (editor.parentNode != nextPage) + nextPage.appendChild(editor); + editor.show(); return; } Splits.show(split); mnuSplitAlign.setAttribute("checked", split.gridLayout == "3rows"); - var _self = this; - split.pages.forEach(function(page, idx) { - var editor = split.editors[idx]; - if (!editor) - return; - var session = _self.getEditorSession(page); - //console.log("switch: ", session); - if (editor.value !== session) - editor.setProperty("value", session); - }); - if (split.clone) { + var _self = this; var page = split.clone; - var editor = page.$editor; + editor = page.$editor; + mnuCloneView.setAttribute("checked", true); if (!page.acesession) { @@ -335,11 +302,6 @@ module.exports = ext.register("ext/splitview/splitview", { } editor.amlEditor.setProperty("value", page.acesession); } - else { - // TODO: please test switching of tabs between normal tabs and split - // views right after uncommenting the line below: - //consolidateEditorSession(next, split.editors[split.pages.indexOf(next)]); - } apf.layout.forceResize(); @@ -347,11 +309,11 @@ module.exports = ext.register("ext/splitview/splitview", { }, changeLayout: function(page, gridLayout) { - var split = Splits.get(page); - if (!split.length || split.gridLayout == gridLayout) + var split = Splits.get(page)[0]; + if (!split || split.gridLayout == gridLayout) return; - Splits.update(split[0], gridLayout); + Splits.update(split, gridLayout); mnuSplitAlign.setAttribute("checked", gridLayout == "3rows"); this.save(); }, @@ -421,9 +383,26 @@ module.exports = ext.register("ext/splitview/splitview", { getEditorSession: function(page) { var doc = page.$doc; + if (!doc) + return null; return doc.acesession || doc.session || null; }, + consolidateEditorSession: function(page, editor) { + var session = this.getEditorSession(page); + if (!session && page.$editor.setDocument) { + var defEditor = page.$editor.amlEditor; + var oldVal = defEditor.value; + page.$editor.setDocument(page.$doc, page.$at); + session = this.getEditorSession(page); + defEditor.setProperty("value", oldVal); + } + editor.setAttribute("model", page.$model); + editor.setAttribute("actiontracker", page.$at); + if (editor.value !== session) + editor.setProperty("value", session); + }, + save: function() { if (!Settings.model) return; From ffb817b958a19581cd1b4eb72e4ba20381c0f298 Mon Sep 17 00:00:00 2001 From: mikedeboer Date: Mon, 19 Dec 2011 15:44:41 +0100 Subject: [PATCH 14/69] changed splitter to proper divider. Fixes #414 --- client/ext/splitview/splitview.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/ext/splitview/splitview.js b/client/ext/splitview/splitview.js index 94cbd538908..df5b722eb5c 100644 --- a/client/ext/splitview/splitview.js +++ b/client/ext/splitview/splitview.js @@ -42,7 +42,7 @@ module.exports = ext.register("ext/splitview/splitview", { var parent = Tabbehaviors.nodes[Tabbehaviors.nodes.length - 1]; this.nodes.push( - parent.appendChild(new apf.splitter()), + parent.appendChild(new apf.divider()), parent.appendChild( (mnuCloneView = new apf.item({ caption : "Clone Editor", From 96dc3936b48b9e9af24a2ec2c907d1eb462bef94 Mon Sep 17 00:00:00 2001 From: mikedeboer Date: Mon, 19 Dec 2011 16:05:30 +0100 Subject: [PATCH 15/69] updated merged look of tabs. Fixes #686 --- client/ext/splitview/splits.js | 16 +++++++++++++++- client/ext/splitview/splitview.css | 3 +-- 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/client/ext/splitview/splits.js b/client/ext/splitview/splits.js index b36e0e3bbd6..ed9493ba4f2 100644 --- a/client/ext/splitview/splits.js +++ b/client/ext/splitview/splits.js @@ -16,6 +16,7 @@ var Splits = []; var EditorClones = {}; var ActiveClass = "splitview_active"; var InactiveClass = "splitview_inactive"; +var NPlusOneClass = "splitview_nplus1"; var SplitView, ActiveSplit; exports.init = function(splitView) { @@ -265,6 +266,7 @@ exports.mutate = function(split, page) { //console.log("setting model of ", editorToUse.id, "to", page.$model.data.xml); SplitView.consolidateEditorSession(page, editorToUse); + setSplitViewStyles(split); this.show(split); } @@ -410,10 +412,22 @@ function onEditorFocus(editor) { function clearSplitViewStyles(splitOrPage) { var pages = (typeof splitOrPage.tagName != "undefined") ? [splitOrPage] : splitOrPage.pages; pages.forEach(function(page) { - apf.setStyleClass(page.$button, null, [ActiveClass, InactiveClass]); + apf.setStyleClass(page.$button, null, [ActiveClass, InactiveClass, NPlusOneClass]); }); } +function setSplitViewStyles(splitOrPage) { + var pages = (typeof splitOrPage.tagName != "undefined") ? [null, splitOrPage] : splitOrPage.pages; + for (var i = 0, l = pages.length; i < l; ++i) { + if (!pages[i]) + continue; + if (i == 0) + apf.setStyleClass(pages[i].$button, null, [NPlusOneClass]); + else + apf.setStyleClass(pages[i].$button, NPlusOneClass, []); + } +} + var searchWindow, gotoLineWindow, searchPos; function correctQuickSearchDialog() { diff --git a/client/ext/splitview/splitview.css b/client/ext/splitview/splitview.css index e3e2d55355f..5742e650e74 100644 --- a/client/ext/splitview/splitview.css +++ b/client/ext/splitview/splitview.css @@ -7,7 +7,6 @@ } -.editor_tab .btnsesssioncontainer div.curbtn.splitview_active .tab_left, -.editor_tab .btnsesssioncontainer div.curbtn.splitview_inactive .tab_left { +.editor_tab .btnsesssioncontainer div.curbtn.splitview_nplus1 .tab_left { background-position: 0 -234px; } \ No newline at end of file From 1f0e66e868b14c45051e296bbf7abde4bda8fcc4 Mon Sep 17 00:00:00 2001 From: mikedeboer Date: Mon, 19 Dec 2011 16:11:52 +0100 Subject: [PATCH 16/69] not doing smart shit anymore, just set the tab from settings. Fixes #689 --- client/ext/splitview/splitview.js | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/client/ext/splitview/splitview.js b/client/ext/splitview/splitview.js index df5b722eb5c..bc03385e9f8 100644 --- a/client/ext/splitview/splitview.js +++ b/client/ext/splitview/splitview.js @@ -473,14 +473,7 @@ module.exports = ext.register("ext/splitview/splitview", { active = Splits.getActive(); } - if (active) { - tabs.set(active.pages[0]); - Splits.update(active); - mnuSplitAlign.setAttribute("checked", active.gridLayout == "3rows"); - mnuCloneView.setAttribute("checked", !!active.clone); - } - else - tabs.set(activePage); + tabs.set(activePage); }, enable : function(){ From 121c50b2ab6e9140141fb36988b7c26fb97ca21b Mon Sep 17 00:00:00 2001 From: mikedeboer Date: Mon, 19 Dec 2011 17:50:58 +0100 Subject: [PATCH 17/69] updated split view creation and update handling to be more robust. Fixes #690 --- client/ext/splitview/splits.js | 100 +++++++++++++++++++++--------- client/ext/splitview/splitview.js | 32 ++-------- 2 files changed, 74 insertions(+), 58 deletions(-) diff --git a/client/ext/splitview/splits.js b/client/ext/splitview/splits.js index ed9493ba4f2..8982de06ee2 100644 --- a/client/ext/splitview/splits.js +++ b/client/ext/splitview/splits.js @@ -87,7 +87,7 @@ exports.create = function(page, gridLayout) { var editor = page.$editor.amlEditor; editor.setAttribute("model", page.$model); editor.setAttribute("actiontracker", page.$at); - SplitView.consolidateEditorSession(page, editor); + exports.consolidateEditorSession(page, editor); var split = { editors: [editor], @@ -104,8 +104,8 @@ exports.show = function(split) { return this; this.update(split); - if (ActiveSplit && ActiveSplit.gridLayout != split.gridLayout) - this.hide(ActiveSplit); + if (ActiveSplit) + this.hide(ActiveSplit, ActiveSplit.gridLayout == split.gridLayout); Grids.show(split.gridLayout); var i, l; @@ -120,7 +120,7 @@ exports.show = function(split) { for (i = 0, l = split.pages.length; i < l; ++i) { split.pages[i].$activateButton(); split.editors[i].show(); - SplitView.consolidateEditorSession(split.pages[i], split.editors[i]); + exports.consolidateEditorSession(split.pages[i], split.editors[i]); } ActiveSplit = split; @@ -128,9 +128,10 @@ exports.show = function(split) { return this; }; -exports.hide = function(split) { +exports.hide = function(split, notGrid) { split = split || ActiveSplit; - Grids.hide(split.gridLayout); + if (!notGrid) + Grids.hide(split.gridLayout); var i, l; for (i = 0, l = split.pages.length; i < l; ++i) split.pages[i].$deactivateButton(); @@ -160,11 +161,6 @@ exports.update = function(split, gridLayout) { // split.editors.length,page.name,split.pages.map(function(page){return page.name})); if (split.pages.length === 1) { var editor = page.$editor.amlEditor; - editor.removeAttribute("model"); - editor.removeAttribute("actiontracker"); - amlPage.appendChild(editor); - editor.show(); - if (EditorClones[editor.tagName]) { for (var clone, i = 0, l = EditorClones[editor.tagName].length; i < l; ++i) { clone = EditorClones[editor.tagName][i]; @@ -172,11 +168,13 @@ exports.update = function(split, gridLayout) { apf.document.body.appendChild(clone); } } + + editor.removeAttribute("model"); + editor.removeAttribute("actiontracker"); + amlPage.appendChild(editor); + editor.show(); - removeEditorListeners(editor); - removeEditorListeners(split.editors[0]); clearSplitViewStyles(page); - Grids.hide(split.gridLayout); if (ActiveSplit === split) @@ -194,6 +192,8 @@ exports.update = function(split, gridLayout) { sortEditorsAndPages(split); //console.log("split editors:", split.editors.length, split.editors.map(function(e) { return e.id; })); Grids.update(gridLayout, split); + // make sure visual styles are OK + setSplitViewStyles(split); // make sure the buttons of the pages in the active split are highlighted if (split === ActiveSplit) { @@ -207,8 +207,8 @@ exports.update = function(split, gridLayout) { exports.mutate = function(split, page) { split = split || split === null ? ActiveSplit : null; var activePage = tabEditors.getPage(); - var pageIdx = split ? split.pages.indexOf(page) : -1; - + var pageIdx = split ? exports.indexOf(split, page) : -1; + // Remove an editor from the split view if (pageIdx > -1) { if (split.clone && split.clone === page) @@ -221,7 +221,7 @@ exports.mutate = function(split, page) { split.editors.splice(editorIdx, 1); editor.removeAttribute("model"); editor.removeAttribute("actiontracker"); - removeEditorListeners(editor); + //removeEditorListeners(editor); editor.hide(); apf.document.body.appendChild(editor); @@ -252,21 +252,20 @@ exports.mutate = function(split, page) { } else { for (var i = 0, l = clones.length; i < l; ++i) { - if (split.editors.indexOf(clones[i]) == -1) { + if (exports.indexOf(split, clones[i]) == -1) { editorToUse = clones[i]; break; } } } - if (!editorToUse && split.editors.indexOf(page.$editor.amlEditor) === -1) + if (!editorToUse && exports.indexOf(split, page.$editor.amlEditor) == -1) editorToUse = page.$editor.amlEditor; split.pages.push(page); split.editors.push(editorToUse); //console.log("setting model of ", editorToUse.id, "to", page.$model.data.xml); - SplitView.consolidateEditorSession(page, editorToUse); + exports.consolidateEditorSession(page, editorToUse); - setSplitViewStyles(split); this.show(split); } @@ -277,16 +276,14 @@ exports.get = function(amlNode) { if (!amlNode) return [].concat(Splits); - var nodeName = amlNode.tagName; var split; var i = 0; var l = Splits.length; var splits = []; - var splitVar = nodeName.indexOf("page") > -1 ? "pages" : "editors"; for (; i < l; ++i) { split = Splits[i]; - if (!split || split[splitVar].indexOf(amlNode) === -1) + if (!split || exports.indexOf(split, amlNode) === -1) continue; splits.push(split); } @@ -306,6 +303,19 @@ exports.getActive = function() { return ActiveSplit || null; }; +/* + * Implemented this function, because Array.indexOf() compares objects with '==' + * instead of '==='! + */ +exports.indexOf = function(split, obj) { + var coll = split[obj.tagName.indexOf("page") > -1 ? "pages" : "editors"]; + for (var i = 0, l = coll.length; i < l; ++i) { + if (coll[i] === obj) + return i; + } + return -1; +} + function sortEditorsAndPages(split) { // lstOpenFiles.$model.data.selectNodes("//file") var pages = tabEditors.getPages(); @@ -315,7 +325,7 @@ function sortEditorsAndPages(split) { //console.log("before sort: ", [].concat(split.pages).map(function(p) { return p.name; }), // [].concat(split.editors).map(function(e) { return e.id; })); for (var i = 0, c = 0, l = pages.length, l2 = split.pages.length; i < l && c < l2; ++i) { - if ((index = split.pages.indexOf(pages[i])) > -1) { + if ((index = exports.indexOf(split, pages[i])) > -1) { //console.log("pushing page at index " + i + " which is in the split at " // + index + ", names " + pages[i].name + ", " + split.pages[index].name); p.push(split.pages[index]); @@ -343,8 +353,14 @@ function createEditorClones(editor) { addEditorListeners.call(this, EditorClones.cloneEditor); } - if (EditorClones[id] && EditorClones[id].length) + if (EditorClones[id] && EditorClones[id].length) { + for (var clone, i = 0, l = EditorClones[id].length; i < l; ++i) { + clone = EditorClones[id][i]; + clone.hide(); + apf.document.body.appendChild(clone); + } return EditorClones[id]; + } addEditorListeners.call(this, editor); @@ -367,6 +383,29 @@ function createEditorClones(editor) { return EditorClones[id]; } + +exports.getEditorSession = function(page) { + var doc = page.$doc; + if (!doc) + return null; + return doc.acesession || doc.session || null; +}; + +exports.consolidateEditorSession = function(page, editor) { + var session = exports.getEditorSession(page); + if (!session && page.$editor.setDocument) { + var defEditor = page.$editor.amlEditor; + var oldVal = defEditor.value; + page.$editor.setDocument(page.$doc, page.$at); + session = exports.getEditorSession(page); + defEditor.setProperty("value", oldVal); + } + editor.setAttribute("model", page.$model); + editor.setAttribute("actiontracker", page.$at); + if (editor.value !== session) + editor.setProperty("value", session); +}; + /** * Add listeners to the editor element, to keep track of the editor's focus. * Each time the focus changes, the tab title color will highlight. @@ -375,7 +414,6 @@ function addEditorListeners(editor) { if (editor.$splitListener) return; editor.addEventListener("focus", editor.$splitListener = function(e) { - //console.log("got focus?", editor.id); onEditorFocus(editor); }); } @@ -383,6 +421,7 @@ function addEditorListeners(editor) { function removeEditorListeners(editor) { if (!editor.$splitListener) return; + console.log("removing event listeners!!"); editor.removeEventListener("focus", editor.$splitListener); delete editor.$splitListener; } @@ -391,7 +430,6 @@ var previousEditor; function onEditorFocus(editor) { var splits = exports.get(editor); - if (Editors.currentEditor.name.indexOf("Code Editor") > -1) { if (!previousEditor) previousEditor = Editors.currentEditor.amlEditor; @@ -399,7 +437,7 @@ function onEditorFocus(editor) { } splits.forEach(function(split) { - var activePage = split.pages[split.editors.indexOf(editor)]; + var activePage = split.pages[exports.indexOf(split, editor)]; split.pages.forEach(function(page) { if (page === activePage) apf.setStyleClass(page.$button, ActiveClass, [InactiveClass]); @@ -432,7 +470,7 @@ var searchWindow, gotoLineWindow, searchPos; function correctQuickSearchDialog() { var editor = Editors.currentEditor.amlEditor; - var pos = !ActiveSplit ? -1 : ActiveSplit.editors.indexOf(editor); + var pos = !ActiveSplit ? -1 : exports.indexOf(ActiveSplit, editor); if (pos == -1) return; @@ -466,7 +504,7 @@ function correctQuickSearchDialog() { function correctGotoLineDialog(e) { var editor = Editors.currentEditor.amlEditor; - var pos = !ActiveSplit ? -1 : ActiveSplit.editors.indexOf(editor); + var pos = !ActiveSplit ? -1 : exports.indexOf(ActiveSplit, editor); if (pos == -1) return; diff --git a/client/ext/splitview/splitview.js b/client/ext/splitview/splitview.js index bc03385e9f8..5cdd77939df 100644 --- a/client/ext/splitview/splitview.js +++ b/client/ext/splitview/splitview.js @@ -85,7 +85,7 @@ module.exports = ext.register("ext/splitview/splitview", { var editor = Editors.currentEditor && Editors.currentEditor.amlEditor; if (!split || !editor) return; - var idx = split.editors.indexOf(editor); + var idx = Splits.indexOf(split, editor); if (idx == -1) return; e.returnValue = split.pages[idx]; @@ -127,7 +127,7 @@ module.exports = ext.register("ext/splitview/splitview", { var pages = tabs.getPages(); var curr = tabs.getPage(); var split = Splits.getActive(); - if (split && split.pages.indexOf(curr) > -1) + if (split && Splits.indexOf(split, curr) > -1) curr = split.pages[bRight ? split.pages.length - 1 : 0]; if (!curr || pages.length == 1) return; @@ -224,7 +224,7 @@ module.exports = ext.register("ext/splitview/splitview", { var bRight = e.dir == "right"; var idx = pages.indexOf(split.pages[bRight ? split.pages.length - 1 : 0]) + (bRight ? 1 : -1); idx = idx < 0 ? maxIdx : idx > maxIdx ? 0 : idx; - if (split.pages.indexOf(pages[idx]) > -1) + if (Splits.indexOf(split, pages[idx]) > -1) return false; // check if the next tab is inside a split as well: @@ -269,7 +269,7 @@ module.exports = ext.register("ext/splitview/splitview", { editor = next.$editor.amlEditor; if (!editor) return; - this.consolidateEditorSession(next, editor); + Splits.consolidateEditorSession(next, editor); var nextPage = next.fake ? next.relPage : next; if (editor.parentNode != nextPage) nextPage.appendChild(editor); @@ -325,7 +325,7 @@ module.exports = ext.register("ext/splitview/splitview", { var split = this.getCloneView(page); var doc = page.$doc; - if (split || !doc || !this.getEditorSession(page)) + if (split || !doc || !Splits.getEditorSession(page)) return; var fake = tabEditors.add("{([@changed] == 1 ? '*' : '') + [@name]}", page.$model.data.getAttribute("path") @@ -381,28 +381,6 @@ module.exports = ext.register("ext/splitview/splitview", { return null; }, - getEditorSession: function(page) { - var doc = page.$doc; - if (!doc) - return null; - return doc.acesession || doc.session || null; - }, - - consolidateEditorSession: function(page, editor) { - var session = this.getEditorSession(page); - if (!session && page.$editor.setDocument) { - var defEditor = page.$editor.amlEditor; - var oldVal = defEditor.value; - page.$editor.setDocument(page.$doc, page.$at); - session = this.getEditorSession(page); - defEditor.setProperty("value", oldVal); - } - editor.setAttribute("model", page.$model); - editor.setAttribute("actiontracker", page.$at); - if (editor.value !== session) - editor.setProperty("value", session); - }, - save: function() { if (!Settings.model) return; From 6afa9095106587451827f811d1724f754214ad4f Mon Sep 17 00:00:00 2001 From: mikedeboer Date: Sat, 24 Dec 2011 16:25:28 +0100 Subject: [PATCH 18/69] selecting tabs in active splitview now focusses on correct editor and changes tab highlight color. fixes #693 --- client/ext/editors/editors.js | 7 ++++--- client/ext/splitview/grids.js | 18 ++++++++++-------- client/ext/splitview/splits.js | 21 ++++++++++++++++----- client/ext/splitview/splitview.js | 5 ++++- 4 files changed, 34 insertions(+), 17 deletions(-) diff --git a/client/ext/editors/editors.js b/client/ext/editors/editors.js index c95741bc637..7443184bd59 100644 --- a/client/ext/editors/editors.js +++ b/client/ext/editors/editors.js @@ -436,12 +436,13 @@ module.exports = ext.register("ext/editors/editors", { if (editorPage.actiontracker != page.$at) editorPage.setAttribute("actiontracker", page.$at); - page.$editor.setDocument && page.$editor.setDocument(page.$doc, page.$at); - ide.dispatchEvent("editorswitch", { + if (ide.dispatchEvent("editorswitch", { previousPage: e.previousPage, nextPage: e.nextPage - }); + }) !== false) { + page.$editor.setDocument && page.$editor.setDocument(page.$doc, page.$at); + } }, afterswitch : function(e) { diff --git a/client/ext/splitview/grids.js b/client/ext/splitview/grids.js index e30a2fc79a4..a804fc1f6dc 100644 --- a/client/ext/splitview/grids.js +++ b/client/ext/splitview/grids.js @@ -133,25 +133,28 @@ var GridLayouts = { } }; +var grids = module.exports = new apf.Class().$init(); + var GridNames = Object.keys(GridLayouts); -var defaultGrid = exports.DEFAULT_GRID = "3cols"; +var defaultGrid = grids.DEFAULT_GRID = "3cols"; + /** * Create the available grids */ -exports.init = function(gridLayout) { +grids.init = function(gridLayout) { gridLayout = gridLayout || defaultGrid; //console.log("init called", gridLayout); createGridNodes(gridLayout); return gridLayout; }; -exports.get = function(name) { +grids.get = function(name) { return GridLayouts[name]; }; -exports.update = function(gridLayout, split) { +grids.update = function(gridLayout, split) { var grid = GridLayouts[gridLayout]; //console.log("update: ", split.pages[0].$pHtmlNode, grid.node, grid.node.parentNode, grid.node.$pHtmlNode); @@ -173,7 +176,7 @@ exports.update = function(gridLayout, split) { splitters[i].hide(); }; -exports.show = function(gridLayout) { +grids.show = function(gridLayout) { GridNames.forEach(function(name) { var grid = GridLayouts[name]; if (!grid.node) @@ -186,7 +189,7 @@ exports.show = function(gridLayout) { }); }; -exports.hide = function(gridLayout) { +grids.hide = function(gridLayout) { gridLayout = gridLayout || defaultGrid; var grid = GridLayouts[gridLayout]; if (!grid || !grid.node) @@ -258,8 +261,7 @@ function createGridNodes(name) { lastEvent = e; if (timeout) return; - if (exports.onresize) - exports.onresize(lastEvent, this); + grids.dispatchEvent("resize", lastEvent, this); timeout = setTimeout(function(){ timeout = null; }, 100); }); } diff --git a/client/ext/splitview/splits.js b/client/ext/splitview/splits.js index 8982de06ee2..a203560a1d0 100644 --- a/client/ext/splitview/splits.js +++ b/client/ext/splitview/splits.js @@ -53,7 +53,7 @@ exports.init = function(splitView) { e.returnValue = correctGotoLineDialog(e); }); - Grids.onresize = function(e, node) { + Grids.addEventListener("resize", function(e, node) { var correct; if (searchWindow && searchWindow.visible) { correct = correctQuickSearchDialog(); @@ -76,7 +76,7 @@ exports.init = function(splitView) { if (typeof correct.left != "undefined") gotoLineWindow.$ext.style.left = correct.left + "px"; } - }; + }); return this; }; @@ -92,6 +92,7 @@ exports.create = function(page, gridLayout) { var split = { editors: [editor], pages: [page], + activePage: 0, gridLayout: gridLayout }; Splits.push(split); @@ -195,6 +196,8 @@ exports.update = function(split, gridLayout) { // make sure visual styles are OK setSplitViewStyles(split); + exports.setActivePage(split); + // make sure the buttons of the pages in the active split are highlighted if (split === ActiveSplit) { for (var i = 0, l = split.pages.length; i < l; ++i) @@ -303,6 +306,11 @@ exports.getActive = function() { return ActiveSplit || null; }; +exports.setActivePage = function(split, activePage) { + var idx = activePage ? exports.indexOf(split, activePage) : split.activePage; + split.editors[idx].focus(); +}; + /* * Implemented this function, because Array.indexOf() compares objects with '==' * instead of '==='! @@ -438,12 +446,15 @@ function onEditorFocus(editor) { splits.forEach(function(split) { var activePage = split.pages[exports.indexOf(split, editor)]; - split.pages.forEach(function(page) { - if (page === activePage) + for (var page, i = 0, l = split.pages.length; i < l; ++i) { + page = split.pages[i]; + if (page === activePage) { + split.activePage = i; apf.setStyleClass(page.$button, ActiveClass, [InactiveClass]); + } else apf.setStyleClass(page.$button, InactiveClass, [ActiveClass]); - }); + } }); } diff --git a/client/ext/splitview/splitview.js b/client/ext/splitview/splitview.js index 5cdd77939df..b29ad186721 100644 --- a/client/ext/splitview/splitview.js +++ b/client/ext/splitview/splitview.js @@ -69,7 +69,7 @@ module.exports = ext.register("ext/splitview/splitview", { ); ide.addEventListener("editorswitch", function(e) { - _self.updateSplitView(e.previousPage, e.nextPage); + return _self.updateSplitView(e.previousPage, e.nextPage); }); ide.addEventListener("closefile", function(e) { @@ -180,6 +180,7 @@ module.exports = ext.register("ext/splitview/splitview", { ret = false; break; } + Splits.setActivePage(split, page); // only the first tab in the split view is the trigger to select all // other tabs as well (because only the page of the first tab is // REALLY shown) @@ -306,6 +307,8 @@ module.exports = ext.register("ext/splitview/splitview", { apf.layout.forceResize(); this.save(); + + return false; }, changeLayout: function(page, gridLayout) { From fa2f418e743e6592e180668e473e314f863e02ba Mon Sep 17 00:00:00 2001 From: mikedeboer Date: Sat, 24 Dec 2011 16:25:49 +0100 Subject: [PATCH 19/69] updated apf --- client/js/apf_release.js | 2 +- support/apf | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/client/js/apf_release.js b/client/js/apf_release.js index 7dede2f6ff7..a229f920e22 100644 --- a/client/js/apf_release.js +++ b/client/js/apf_release.js @@ -6748,7 +6748,7 @@ delete _self.$btnControl[aml.$uniqueId];if(div&&div.parentNode){div.parentNode.i }var aml=_self.$lastPosition!==null?apf.findHost(_self.$lastPosition||div.nextSibling):null; if(started&&aml!=_self.nextSibling){apf.tween.single(_self.$button,{steps:20,interval:10,from:_self.$button.offsetLeft,to:_self.$lastLeft||div.offsetLeft,type:"left",control:_self.$btnControl[_self.$uniqueId]={},anim:apf.tween.easeInOutCubic,onstop:function(){},onfinish:function(){oHtml.style.position=oHtml.style.zIndex=oHtml.style.top=oHtml.style.left=""; _self.parentNode.insertBefore(_self,aml);div.parentNode.removeChild(div);delete _self.$btnControl[_self.$uniqueId]; -_self.parentNode.dispatchEvent("tabselectmouseup");}});}else{oHtml.style.position=oHtml.style.zIndex=oHtml.style.top=oHtml.style.left=""; +}});}else{oHtml.style.position=oHtml.style.zIndex=oHtml.style.top=oHtml.style.left=""; div.parentNode.removeChild(div);}apf.removeListener(document,"mouseup",mUp);apf.removeListener(document,"mousemove",mMove); });}}};this.$btnUp=function(oHtml){this.parentNode.$setStyleClass(oHtml,"",["down"],true); if(this.disabled){return;}if(this.parentNode.$order&&this.$btnPressed){this.$dragging=false; diff --git a/support/apf b/support/apf index 2560b762b2b..7bc3c9a938b 160000 --- a/support/apf +++ b/support/apf @@ -1 +1 @@ -Subproject commit 2560b762b2b0e5a8b46f8a4062f927a9a9d239f4 +Subproject commit 7bc3c9a938b377061c0ccb1907fbd4114d0e168e From f44c55fefbd2c03e8ba77856abc7eac23b68466d Mon Sep 17 00:00:00 2001 From: mikedeboer Date: Mon, 26 Dec 2011 14:36:59 +0100 Subject: [PATCH 20/69] fixed tab cycling with splits and correctly loading initial state of tabs and splits from settings --- client/ext/splitview/splitview.js | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/client/ext/splitview/splitview.js b/client/ext/splitview/splitview.js index b29ad186721..5c9b4094227 100644 --- a/client/ext/splitview/splitview.js +++ b/client/ext/splitview/splitview.js @@ -219,14 +219,14 @@ module.exports = ext.register("ext/splitview/splitview", { if (!split) return; if (split.pages.length == pages.length) - return false; + return (e.returnValue = false); var maxIdx = pages.length - 1; var bRight = e.dir == "right"; var idx = pages.indexOf(split.pages[bRight ? split.pages.length - 1 : 0]) + (bRight ? 1 : -1); idx = idx < 0 ? maxIdx : idx > maxIdx ? 0 : idx; if (Splits.indexOf(split, pages[idx]) > -1) - return false; + return (e.returnValue = false); // check if the next tab is inside a split as well: split = Splits.get(pages[idx])[0]; @@ -454,7 +454,10 @@ module.exports = ext.register("ext/splitview/splitview", { active = Splits.getActive(); } - tabs.set(activePage); + if (!active || Splits.indexOf(active, activePage) == -1) + tabs.set(activePage); + else + Splits.show(active); }, enable : function(){ From 619b2b4b07eb0660ab27ffdecda6611ee375c6c0 Mon Sep 17 00:00:00 2001 From: mikedeboer Date: Mon, 26 Dec 2011 14:46:02 +0100 Subject: [PATCH 21/69] small JS error fix --- client/ext/splitview/splits.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/client/ext/splitview/splits.js b/client/ext/splitview/splits.js index a203560a1d0..87b63ed3874 100644 --- a/client/ext/splitview/splits.js +++ b/client/ext/splitview/splits.js @@ -308,6 +308,8 @@ exports.getActive = function() { exports.setActivePage = function(split, activePage) { var idx = activePage ? exports.indexOf(split, activePage) : split.activePage; + if (idx == -1) + return; split.editors[idx].focus(); }; From 807e0a168d467dd0dafb25d5575f34621f69712d Mon Sep 17 00:00:00 2001 From: mikedeboer Date: Mon, 9 Jan 2012 13:19:32 +0100 Subject: [PATCH 22/69] fixed editor reference error and other minor improvements --- client/ext/splitview/splits.js | 16 +++++++++++----- client/ext/splitview/splitview.js | 24 ++++++++++++++---------- 2 files changed, 25 insertions(+), 15 deletions(-) diff --git a/client/ext/splitview/splits.js b/client/ext/splitview/splits.js index 87b63ed3874..fb706ff957b 100644 --- a/client/ext/splitview/splits.js +++ b/client/ext/splitview/splits.js @@ -261,8 +261,11 @@ exports.mutate = function(split, page) { } } } - if (!editorToUse && exports.indexOf(split, page.$editor.amlEditor) == -1) - editorToUse = page.$editor.amlEditor; + if (!editorToUse && exports.indexOf(split, clones.original) == -1) + editorToUse = clones.original; + + if (!editorToUse) + throw new Error("Splitview fatal error: no editor available to use."); split.pages.push(page); split.editors.push(editorToUse); @@ -310,7 +313,7 @@ exports.setActivePage = function(split, activePage) { var idx = activePage ? exports.indexOf(split, activePage) : split.activePage; if (idx == -1) return; - split.editors[idx].focus(); + (split.editors[idx] || split.editors[0]).focus(); }; /* @@ -375,6 +378,7 @@ function createEditorClones(editor) { addEditorListeners.call(this, editor); EditorClones[id] = []; + EditorClones[id].original = editor; for (var i = 0; i < 2; ++i) { editor = editor.cloneNode(true); @@ -410,6 +414,8 @@ exports.consolidateEditorSession = function(page, editor) { session = exports.getEditorSession(page); defEditor.setProperty("value", oldVal); } + if (!editor) + console.trace(); editor.setAttribute("model", page.$model); editor.setAttribute("actiontracker", page.$at); if (editor.value !== session) @@ -431,7 +437,7 @@ function addEditorListeners(editor) { function removeEditorListeners(editor) { if (!editor.$splitListener) return; - console.log("removing event listeners!!"); + //console.log("removing event listeners!!"); editor.removeEventListener("focus", editor.$splitListener); delete editor.$splitListener; } @@ -440,7 +446,7 @@ var previousEditor; function onEditorFocus(editor) { var splits = exports.get(editor); - if (Editors.currentEditor.name.indexOf("Code Editor") > -1) { + if (Editors.currentEditor && Editors.currentEditor.name.indexOf("Code Editor") > -1) { if (!previousEditor) previousEditor = Editors.currentEditor.amlEditor; Editors.currentEditor.amlEditor = editor; diff --git a/client/ext/splitview/splitview.js b/client/ext/splitview/splitview.js index 5c9b4094227..3cf8946478b 100644 --- a/client/ext/splitview/splitview.js +++ b/client/ext/splitview/splitview.js @@ -39,6 +39,7 @@ module.exports = ext.register("ext/splitview/splitview", { apf.importCssString(css || ""); var _self = this; + var tabs = tabEditors; // localize global 'tabEditors' var parent = Tabbehaviors.nodes[Tabbehaviors.nodes.length - 1]; this.nodes.push( @@ -50,9 +51,9 @@ module.exports = ext.register("ext/splitview/splitview", { checked : false, onclick : function() { if (this.checked) - _self.startCloneView(tabEditors.contextPage); + _self.startCloneView(tabs.contextPage); else - _self.endCloneView(tabEditors.contextPage); + _self.endCloneView(tabs.contextPage); } })) ), @@ -62,7 +63,7 @@ module.exports = ext.register("ext/splitview/splitview", { type : "check", checked : true, onclick : function() { - _self.changeLayout(tabEditors.contextPage, this.checked ? "3rows" : "3cols"); + _self.changeLayout(tabs.contextPage, this.checked ? "3rows" : "3cols"); } })) ) @@ -91,11 +92,11 @@ module.exports = ext.register("ext/splitview/splitview", { e.returnValue = split.pages[idx]; }); - tabEditors.addEventListener("tabselectclick", function(e) { + tabs.addEventListener("tabselectclick", function(e) { return _self.onTabClick(e); }); - tabEditors.addEventListener("tabselectmouseup", function(e) { + tabs.addEventListener("tabselectmouseup", function(e) { var page = this.$activepage; var split = Splits.get(page)[0]; if (split) @@ -168,7 +169,8 @@ module.exports = ext.register("ext/splitview/splitview", { */ onTabClick: function(e) { var page = e.page; - var activePage = tabEditors.getPage(); + var tabs = tabEditors; + var activePage = tabs.getPage(); var shiftKey = e.htmlEvent.shiftKey; var ret = null; var split = Splits.get(activePage)[0]; @@ -185,7 +187,7 @@ module.exports = ext.register("ext/splitview/splitview", { // other tabs as well (because only the page of the first tab is // REALLY shown) if (ret !== false && page !== split.pages[0]) { - tabEditors.set(split.pages[0]); + tabs.set(split.pages[0]); ret = false; } @@ -346,7 +348,7 @@ module.exports = ext.register("ext/splitview/splitview", { fake.setAttribute("model", fake.$model = page.$model); page.addEventListener("DOMNodeRemovedFromDocument", function(e) { - if (typeof tabEditors == "undefined") + if (typeof tabEditors == "undefined" || !fake || !fake.parentNode) return; tabEditors.remove(fake); }); @@ -358,6 +360,8 @@ module.exports = ext.register("ext/splitview/splitview", { split = Splits.get(fake)[0]; split.clone = fake; + Splits.update(split); + this.save(); return fake; @@ -409,14 +413,14 @@ module.exports = ext.register("ext/splitview/splitview", { restore: function(settings) { // no tabs open... don't bother ;) - if (tabEditors.childNodes.length <= 1) + var tabs = tabEditors; + if (tabs.childNodes.length <= 1) return; var nodes = settings.selectNodes("splits/split"); if (!nodes || !nodes.length) return; - var tabs = tabEditors; var activePage = tabs.getPage(); var i, l, j, l2, ids, active, page, pages, pageSet, gridLayout; for (i = 0, l = nodes.length; i < l; ++i) { From 99406e11f228992cdbda9eb701182426629f7b4a Mon Sep 17 00:00:00 2001 From: mikedeboer Date: Mon, 9 Jan 2012 14:30:46 +0100 Subject: [PATCH 23/69] udpate active splitview upon fileclose, fixes #703 --- client/ext/splitview/splitview.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/client/ext/splitview/splitview.js b/client/ext/splitview/splitview.js index 3cf8946478b..c5b1b5dd99e 100644 --- a/client/ext/splitview/splitview.js +++ b/client/ext/splitview/splitview.js @@ -143,7 +143,7 @@ module.exports = ext.register("ext/splitview/splitview", { return; // pass in null to mutate the active split view Splits.mutate(null, pages[idx]); - Splits.update(); + //Splits.update(); this.save(); return false; }, @@ -158,6 +158,7 @@ module.exports = ext.register("ext/splitview/splitview", { var splits = Splits.get(page); for (var i = 0, l = splits.length; i < l; ++i) Splits.mutate(splits[i], page); + Splits.update(); this.save(); }, From 986db06ba9ada70b39d3cff566bea91b674972c4 Mon Sep 17 00:00:00 2001 From: mikedeboer Date: Mon, 9 Jan 2012 14:34:56 +0100 Subject: [PATCH 24/69] undo commented out splits update, broke tab merging --- client/ext/splitview/splitview.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/ext/splitview/splitview.js b/client/ext/splitview/splitview.js index c5b1b5dd99e..25694f9f98d 100644 --- a/client/ext/splitview/splitview.js +++ b/client/ext/splitview/splitview.js @@ -143,7 +143,7 @@ module.exports = ext.register("ext/splitview/splitview", { return; // pass in null to mutate the active split view Splits.mutate(null, pages[idx]); - //Splits.update(); + Splits.update(); this.save(); return false; }, From f3823d4020729dc4017130f075367b135e74ae53 Mon Sep 17 00:00:00 2001 From: mikedeboer Date: Wed, 11 Jan 2012 15:49:09 +0100 Subject: [PATCH 25/69] fixed tabs context menu disappearing after one-time usage --- client/ext/splitview/splitview.js | 30 +++++--- client/ext/tabbehaviors/tabbehaviors.js | 98 +++++++++++++------------ 2 files changed, 70 insertions(+), 58 deletions(-) diff --git a/client/ext/splitview/splitview.js b/client/ext/splitview/splitview.js index 25694f9f98d..b775f3b7e28 100644 --- a/client/ext/splitview/splitview.js +++ b/client/ext/splitview/splitview.js @@ -11,8 +11,8 @@ define(function(require, exports, module) { var ide = require("core/ide"); var ext = require("core/ext"); var css = require("text!ext/splitview/splitview.css"); -var Tabbehaviors = require("ext/tabbehaviors/tabbehaviors"); var Editors = require("ext/editors/editors"); +var Tabbehaviors = require("ext/tabbehaviors/tabbehaviors"); var Settings = require("ext/settings/settings"); var Splits = require("ext/splitview/splits"); @@ -41,7 +41,7 @@ module.exports = ext.register("ext/splitview/splitview", { var _self = this; var tabs = tabEditors; // localize global 'tabEditors' - var parent = Tabbehaviors.nodes[Tabbehaviors.nodes.length - 1]; + var parent = Tabbehaviors.menu; this.nodes.push( parent.appendChild(new apf.divider()), parent.appendChild( @@ -70,6 +70,7 @@ module.exports = ext.register("ext/splitview/splitview", { ); ide.addEventListener("editorswitch", function(e) { + // the return value actually does something! return _self.updateSplitView(e.previousPage, e.nextPage); }); @@ -123,18 +124,27 @@ module.exports = ext.register("ext/splitview/splitview", { }, mergeTab: function(dir) { - var bRight = dir == "right"; - var tabs = tabEditors; - var pages = tabs.getPages(); - var curr = tabs.getPage(); - var split = Splits.getActive(); + var bRight = dir == "right"; + var tabs = tabEditors; + var pages = tabs.getPages(); + var curr = tabs.getPage(); + var split = Splits.getActive(); + var splitLen = split ? split.pages.length : 0; if (split && Splits.indexOf(split, curr) > -1) - curr = split.pages[bRight ? split.pages.length - 1 : 0]; + curr = split.pages[bRight ? splitLen - 1 : 0]; if (!curr || pages.length == 1) return; - var currIdx = pages.indexOf(curr); - var idx = currIdx + (bRight ? 1 : -1); + var idx; + if (splitLen == 3) { + // if the max amount of tabs has been reached inside a split view, + // then the user may remove the last or first tab from it. + idx = pages.indexOf(split.pages[bRight ? splitLen - 1 : 0]); + } + else { + var currIdx = pages.indexOf(curr); + idx = currIdx + (bRight ? 1 : -1); + } if (idx < 0 || idx > pages.length - 1) return; diff --git a/client/ext/tabbehaviors/tabbehaviors.js b/client/ext/tabbehaviors/tabbehaviors.js index c0f187c2230..fbeb3e6d7be 100644 --- a/client/ext/tabbehaviors/tabbehaviors.js +++ b/client/ext/tabbehaviors/tabbehaviors.js @@ -50,72 +50,74 @@ module.exports = ext.register("ext/tabbehaviors/tabbehaviors", { init : function(amlNode){ var _self = this; + var tabs = tabEditors; + var menu = mnuTabs; this.nodes.push( - mnuTabs.appendChild(new apf.item({ + menu.appendChild(new apf.item({ caption : "Reveal in File Tree", onclick : function() { - _self.revealtab(tabEditors.contextPage); + _self.revealtab(tabs.contextPage); }, disabled : "{!!!tabEditors.activepage}" })), - mnuTabs.appendChild(new apf.item({ + menu.appendChild(new apf.item({ caption : "Close Tab", onclick : function() { - _self.closetab(tabEditors.contextPage); + _self.closetab(tabs.contextPage); }, disabled : "{!!!tabEditors.activepage}" })), - mnuTabs.appendChild(new apf.item({ + menu.appendChild(new apf.item({ caption : "Close All Tabs", onclick : this.closealltabs.bind(this), disabled : "{!!!tabEditors.activepage}" })), - mnuTabs.appendChild(new apf.item({ + menu.appendChild(new apf.item({ caption : "Close All But Current Tab", onclick : function() { _self.closeallbutme(); }, disabled : "{!!!tabEditors.activepage}" - })), - //mnuTabs.appendChild(new apf.divider()), - apf.document.body.appendChild(new apf.menu({ - id : "mnuContextTabs", - childNodes : [ - new apf.item({ - caption : "Reveal in File Tree", - onclick : function() { - _self.revealtab(tabEditors.contextPage); - } - }), - new apf.item({ - caption : "Close Tab", - onclick : function() { - _self.closetab(tabEditors.contextPage); - } - }), - new apf.item({ - caption : "Close All Tabs", - onclick : this.closealltabs.bind(this) - }), - new apf.item({ - caption : "Close Other Tabs", - onclick : function() { - _self.closeallbutme(tabEditors.contextPage); - } - }) - ] })) ); + this.menu = apf.document.body.appendChild(new apf.menu({ + id : "mnuContextTabs", + childNodes : [ + new apf.item({ + caption : "Reveal in File Tree", + onclick : function() { + _self.revealtab(tabs.contextPage); + } + }), + new apf.item({ + caption : "Close Tab", + onclick : function() { + _self.closetab(tabs.contextPage); + } + }), + new apf.item({ + caption : "Close All Tabs", + onclick : this.closealltabs.bind(this) + }), + new apf.item({ + caption : "Close Other Tabs", + onclick : function() { + _self.closeallbutme(tabs.contextPage); + } + }) + ] + })); - this.hotitems.revealtab = [this.nodes[0], mnuContextTabs.childNodes[0]]; - this.hotitems.closetab = [this.nodes[1], mnuContextTabs.childNodes[1]]; - this.hotitems.closealltabs = [this.nodes[2], mnuContextTabs.childNodes[2]]; - this.hotitems.closeallbutme = [this.nodes[3], mnuContextTabs.childNodes[3]]; + this.hotitems.revealtab = [this.nodes[0], this.menu.childNodes[0]]; + this.hotitems.closetab = [this.nodes[1], this.menu.childNodes[1]]; + this.hotitems.closealltabs = [this.nodes[2], this.menu.childNodes[2]]; + this.hotitems.closeallbutme = [this.nodes[3], this.menu.childNodes[3]]; + this.nodes = this.nodes.concat(this.menu.childNodes); - tabEditors.setAttribute("contextmenu", "mnuContextTabs"); + tabs.setAttribute("contextmenu", "mnuContextTabs"); - tabEditors.addEventListener("close", function(e) { + tabs.addEventListener("close", function(e) { if (!e || !e.htmlEvent) return; var page = e.page; @@ -128,14 +130,14 @@ module.exports = ext.register("ext/tabbehaviors/tabbehaviors", { } }); - tabEditors.addEventListener("DOMNodeInserted", function(e) { + tabs.addEventListener("DOMNodeInserted", function(e) { var page = e.currentTarget; if (page.localName != "page" || e.relatedNode != this || page.nodeType != 1) return; if (e.$isMoveWithinParent) { if (page.$tabMenu) { - mnuTabs.insertBefore(page.$tabMenu, + menu.insertBefore(page.$tabMenu, page.nextSibling ? page.nextSibling.$tabMenu : null); _self.updateState(); @@ -145,7 +147,7 @@ module.exports = ext.register("ext/tabbehaviors/tabbehaviors", { _self.addItem(page); }); - tabEditors.addEventListener("DOMNodeRemoved", function(e) { + tabs.addEventListener("DOMNodeRemoved", function(e) { if (e.$doOnlyAdmin) return; @@ -160,7 +162,7 @@ module.exports = ext.register("ext/tabbehaviors/tabbehaviors", { }); var cycleKeyPressed, cycleKey = apf.isMac ? 18 : 17; - tabEditors.addEventListener("afterswitch", function(e) { + tabs.addEventListener("afterswitch", function(e) { var page = e.nextPage; if (!cycleKeyPressed) { @@ -169,8 +171,8 @@ module.exports = ext.register("ext/tabbehaviors/tabbehaviors", { } }); - tabEditors.addEventListener("close", function(e) { - if (tabEditors.getPage() == e.page) + tabs.addEventListener("close", function(e) { + if (tabs.getPage() == e.page) this.nextTabInLine = _self.accessed[_self.accessed.length - _self.$tabAccessCycle]; }); @@ -181,7 +183,7 @@ module.exports = ext.register("ext/tabbehaviors/tabbehaviors", { apf.addEventListener("keyup", function(eInfo) { if (eInfo.keyCode == cycleKey && cycleKeyPressed) { - var page = tabEditors.getPage(); + var page = tabs.getPage(); if (page) { _self.accessed.remove(page); _self.accessed.push(page); @@ -191,7 +193,7 @@ module.exports = ext.register("ext/tabbehaviors/tabbehaviors", { } }); - tabEditors.addEventListener("aftersavedialogcancel", function(e) { + tabs.addEventListener("aftersavedialogcancel", function(e) { if (!_self.changedPages) return From 3a3f05ef29b111d5636bf4eff025cf274c31b6d1 Mon Sep 17 00:00:00 2001 From: mikedeboer Date: Mon, 16 Jan 2012 11:45:06 +0100 Subject: [PATCH 26/69] refactored splits structure to combine page and editor instances into pairs --- client/ext/splitview/grids.js | 10 ++-- client/ext/splitview/splits.js | 91 ++++++++++++++++++------------- client/ext/splitview/splitview.js | 37 ++++++++----- 3 files changed, 80 insertions(+), 58 deletions(-) diff --git a/client/ext/splitview/grids.js b/client/ext/splitview/grids.js index a804fc1f6dc..76dbb95db1a 100644 --- a/client/ext/splitview/grids.js +++ b/client/ext/splitview/grids.js @@ -157,18 +157,18 @@ grids.get = function(name) { grids.update = function(gridLayout, split) { var grid = GridLayouts[gridLayout]; - //console.log("update: ", split.pages[0].$pHtmlNode, grid.node, grid.node.parentNode, grid.node.$pHtmlNode); + //console.log("update: ", split.pairs[0].page.$pHtmlNode, grid.node, grid.node.parentNode, grid.node.$pHtmlNode); grid.node.show(); // attach the grid layout to the first page of the splitview... - var page = split.pages[0]; + var page = split.pairs[0].page; var amlPage = page.fake ? page.relPage : page; amlPage.appendChild(grid.node); - //console.log(split.editors.map(function(editor){return editor.$ext;}), grid.insertPoints); + //console.log(split.pairs.map(function(pair){return pair.editor.$ext;}), grid.insertPoints); var i = 0; - var l = split.editors.length; + var l = split.pairs.length; for (; i < l; ++i) - insertEditorAt(grid.node, split.editors[i], [].concat(grid.insertPoints[i])); + insertEditorAt(grid.node, split.pairs[i].editor, [].concat(grid.insertPoints[i])); // hide splitters that we don't need to see anymore var splitters = grid.splitters || grid.node.selectNodes("splitter"); diff --git a/client/ext/splitview/splits.js b/client/ext/splitview/splits.js index fb706ff957b..12b916cc36b 100644 --- a/client/ext/splitview/splits.js +++ b/client/ext/splitview/splits.js @@ -90,8 +90,10 @@ exports.create = function(page, gridLayout) { exports.consolidateEditorSession(page, editor); var split = { - editors: [editor], - pages: [page], + pairs: [{ + page: page, + editor: editor + }], activePage: 0, gridLayout: gridLayout }; @@ -114,14 +116,14 @@ exports.show = function(split) { Splits.forEach(function(aSplit) { if (aSplit === split) return; - for (i = 0, l = aSplit.pages.length; i < l; ++i) - aSplit.pages[i].$deactivateButton(); + for (i = 0, l = aSplit.pairs.length; i < l; ++i) + aSplit.pairs[i].page.$deactivateButton(); }); - //console.log("pages",split.pages.map(function(page){return page.name;})); - for (i = 0, l = split.pages.length; i < l; ++i) { - split.pages[i].$activateButton(); - split.editors[i].show(); - exports.consolidateEditorSession(split.pages[i], split.editors[i]); + //console.log("pages",split.pairs.map(function(pair){return pair.page.name;})); + for (i = 0, l = split.pairs.length; i < l; ++i) { + split.pairs[i].page.$activateButton(); + split.pairs[i].editor.show(); + exports.consolidateEditorSession(split.pairs[i].page, split.pairs[i].editor); } ActiveSplit = split; @@ -134,10 +136,10 @@ exports.hide = function(split, notGrid) { if (!notGrid) Grids.hide(split.gridLayout); var i, l; - for (i = 0, l = split.pages.length; i < l; ++i) - split.pages[i].$deactivateButton(); - for (i = 0, l = split.editors.length; i < l; ++i) - split.editors[i].hide(); + for (i = 0, l = split.pairs.length; i < l; ++i) { + split.pairs[i].page.$deactivateButton(); + split.pairs[i].editor.hide(); + } if (split === ActiveSplit) ActiveSplit = null; @@ -153,14 +155,14 @@ exports.update = function(split, gridLayout) { return; gridLayout = Grids.init(gridLayout || split.gridLayout); - var page = split.pages[0]; + var page = split.pairs[0].page; var amlPage = page.fake ? page.relPage : page; split.gridLayout = gridLayout; // destroy the split view if it contains NOT more than 1 editor. - //console.log("number of pages in split view:", split.pages.length,"vs editors:", + //console.log("number of pages in split view:", split.pairs.length,"vs editors:", // split.editors.length,page.name,split.pages.map(function(page){return page.name})); - if (split.pages.length === 1) { + if (split.pairs.length === 1) { var editor = page.$editor.amlEditor; if (EditorClones[editor.tagName]) { for (var clone, i = 0, l = EditorClones[editor.tagName].length; i < l; ++i) { @@ -191,7 +193,7 @@ exports.update = function(split, gridLayout) { // sort the editors and pages before being added to the grid sortEditorsAndPages(split); - //console.log("split editors:", split.editors.length, split.editors.map(function(e) { return e.id; })); + //console.log("split editors:", split.pairs.length, split.pairs.map(function(pair) { return pair.editor.id; })); Grids.update(gridLayout, split); // make sure visual styles are OK setSplitViewStyles(split); @@ -200,8 +202,8 @@ exports.update = function(split, gridLayout) { // make sure the buttons of the pages in the active split are highlighted if (split === ActiveSplit) { - for (var i = 0, l = split.pages.length; i < l; ++i) - split.pages[i].$activateButton(); + for (var i = 0, l = split.pairs.length; i < l; ++i) + split.pairs[i].page.$activateButton(); } return this; @@ -210,18 +212,17 @@ exports.update = function(split, gridLayout) { exports.mutate = function(split, page) { split = split || split === null ? ActiveSplit : null; var activePage = tabEditors.getPage(); - var pageIdx = split ? exports.indexOf(split, page) : -1; + var pairIdx = split ? exports.indexOf(split, page) : -1; // Remove an editor from the split view - if (pageIdx > -1) { + if (pairIdx > -1) { if (split.clone && split.clone === page) SplitView.endCloneView(page); - var editorIdx = pageIdx; - split.pages.splice(pageIdx, 1); + var editor = split.pairs[pairIdx].editor; + + split.pairs.splice(pairIdx, 1); - var editor = split.editors[editorIdx]; - split.editors.splice(editorIdx, 1); editor.removeAttribute("model"); editor.removeAttribute("actiontracker"); //removeEditorListeners(editor); @@ -231,13 +232,13 @@ exports.mutate = function(split, page) { page.$deactivateButton(); clearSplitViewStyles(page); editor.hide(); - if (tabEditors.getPage() !== split.pages[0]) - tabEditors.set(split.pages[0]); + if (tabEditors.getPage() !== split.pairs[0].page) + tabEditors.set(split.pairs[0].page); this.update(split); } // Add an editor to the split view - else if (!split || split.editors.length < 3) { + else if (!split || split.pairs.length < 3) { var clones = createEditorClones.call(this, page.$editor.amlEditor); if (!split) { @@ -267,8 +268,10 @@ exports.mutate = function(split, page) { if (!editorToUse) throw new Error("Splitview fatal error: no editor available to use."); - split.pages.push(page); - split.editors.push(editorToUse); + split.pairs.push({ + page: page, + editor: editorToUse + }); //console.log("setting model of ", editorToUse.id, "to", page.$model.data.xml); exports.consolidateEditorSession(page, editorToUse); @@ -313,7 +316,7 @@ exports.setActivePage = function(split, activePage) { var idx = activePage ? exports.indexOf(split, activePage) : split.activePage; if (idx == -1) return; - (split.editors[idx] || split.editors[0]).focus(); + (split.pairs[idx] ? split.pairs[idx].editor : split.pairs[0].editor).focus(); }; /* @@ -321,9 +324,9 @@ exports.setActivePage = function(split, activePage) { * instead of '==='! */ exports.indexOf = function(split, obj) { - var coll = split[obj.tagName.indexOf("page") > -1 ? "pages" : "editors"]; - for (var i = 0, l = coll.length; i < l; ++i) { - if (coll[i] === obj) + var type = obj.tagName.indexOf("page") > -1 ? "page" : "editor"; + for (var i = 0, l = split.pairs.length; i < l; ++i) { + if (split.pairs[i][type] === obj) return i; } return -1; @@ -335,6 +338,12 @@ function sortEditorsAndPages(split) { var p = []; var e = []; var index; + split.pairs.sort(function(pair1, pair2) { + var idx1 = pages.indexOf(pair1.page); + var idx2 = pages.indexOf(pair2.page); + return idx1 > idx2 ? 1 : idx1 < idx2 ? -1 : 0; + }); + return; //console.log("before sort: ", [].concat(split.pages).map(function(p) { return p.name; }), // [].concat(split.editors).map(function(e) { return e.id; })); for (var i = 0, c = 0, l = pages.length, l2 = split.pages.length; i < l && c < l2; ++i) { @@ -453,9 +462,9 @@ function onEditorFocus(editor) { } splits.forEach(function(split) { - var activePage = split.pages[exports.indexOf(split, editor)]; - for (var page, i = 0, l = split.pages.length; i < l; ++i) { - page = split.pages[i]; + var activePage = split.pairs[exports.indexOf(split, editor)].page; + for (var page, i = 0, l = split.pairs.length; i < l; ++i) { + page = split.pairs[i].page; if (page === activePage) { split.activePage = i; apf.setStyleClass(page.$button, ActiveClass, [InactiveClass]); @@ -467,14 +476,18 @@ function onEditorFocus(editor) { } function clearSplitViewStyles(splitOrPage) { - var pages = (typeof splitOrPage.tagName != "undefined") ? [splitOrPage] : splitOrPage.pages; + var pages = (typeof splitOrPage.tagName != "undefined") + ? [splitOrPage] + : splitOrPage.pairs.map(function(pair) { return pair.page; }); pages.forEach(function(page) { apf.setStyleClass(page.$button, null, [ActiveClass, InactiveClass, NPlusOneClass]); }); } function setSplitViewStyles(splitOrPage) { - var pages = (typeof splitOrPage.tagName != "undefined") ? [null, splitOrPage] : splitOrPage.pages; + var pages = (typeof splitOrPage.tagName != "undefined") + ? [null, splitOrPage] + : splitOrPage.pairs.map(function(pair) { return pair.page; }); for (var i = 0, l = pages.length; i < l; ++i) { if (!pages[i]) continue; diff --git a/client/ext/splitview/splitview.js b/client/ext/splitview/splitview.js index b775f3b7e28..ef9d9ea3b3a 100644 --- a/client/ext/splitview/splitview.js +++ b/client/ext/splitview/splitview.js @@ -90,7 +90,7 @@ module.exports = ext.register("ext/splitview/splitview", { var idx = Splits.indexOf(split, editor); if (idx == -1) return; - e.returnValue = split.pages[idx]; + e.returnValue = split.pairs[idx].page; }); tabs.addEventListener("tabselectclick", function(e) { @@ -104,6 +104,15 @@ module.exports = ext.register("ext/splitview/splitview", { Splits.update(split); }); + tabs.addEventListener("reorder", function(e) { + var split = Splits.get(e.page); + console.log("got split",split,Splits.getActive()) + if (Splits.isActive(split)) { + console.log("UPDATING!!"); + Splits.show(split); + } + }); + ide.addEventListener("loadsettings", function(e) { if (!e.model || !e.model.data) return; @@ -129,9 +138,9 @@ module.exports = ext.register("ext/splitview/splitview", { var pages = tabs.getPages(); var curr = tabs.getPage(); var split = Splits.getActive(); - var splitLen = split ? split.pages.length : 0; + var splitLen = split ? split.pairs.length : 0; if (split && Splits.indexOf(split, curr) > -1) - curr = split.pages[bRight ? splitLen - 1 : 0]; + curr = split.pairs[bRight ? splitLen - 1 : 0].page; if (!curr || pages.length == 1) return; @@ -139,7 +148,7 @@ module.exports = ext.register("ext/splitview/splitview", { if (splitLen == 3) { // if the max amount of tabs has been reached inside a split view, // then the user may remove the last or first tab from it. - idx = pages.indexOf(split.pages[bRight ? splitLen - 1 : 0]); + idx = pages.indexOf(split.pairs[bRight ? splitLen - 1 : 0].page); } else { var currIdx = pages.indexOf(curr); @@ -187,8 +196,8 @@ module.exports = ext.register("ext/splitview/splitview", { var split = Splits.get(activePage)[0]; if (split && !shiftKey) { - for (var i = 0, l = split.pages.length; i < l; ++i) { - if (split.pages[i] !== activePage) + for (var i = 0, l = split.pairs.length; i < l; ++i) { + if (split.pairs[i].page !== activePage) continue; ret = false; break; @@ -197,14 +206,14 @@ module.exports = ext.register("ext/splitview/splitview", { // only the first tab in the split view is the trigger to select all // other tabs as well (because only the page of the first tab is // REALLY shown) - if (ret !== false && page !== split.pages[0]) { - tabs.set(split.pages[0]); + if (ret !== false && page !== split.pairs[0].page) { + tabs.set(split.pairs[0].page); ret = false; } if (!shiftKey) return true; - + return ret; } else if (shiftKey) { @@ -231,12 +240,12 @@ module.exports = ext.register("ext/splitview/splitview", { var split = Splits.getActive(); if (!split) return; - if (split.pages.length == pages.length) + if (split.pairs.length == pages.length) return (e.returnValue = false); var maxIdx = pages.length - 1; var bRight = e.dir == "right"; - var idx = pages.indexOf(split.pages[bRight ? split.pages.length - 1 : 0]) + (bRight ? 1 : -1); + var idx = pages.indexOf(split.pairs[bRight ? split.pairs.length - 1 : 0].page) + (bRight ? 1 : -1); idx = idx < 0 ? maxIdx : idx > maxIdx ? 0 : idx; if (Splits.indexOf(split, pages[idx]) > -1) return (e.returnValue = false); @@ -244,7 +253,7 @@ module.exports = ext.register("ext/splitview/splitview", { // check if the next tab is inside a split as well: split = Splits.get(pages[idx])[0]; if (split) - e.returnValue = pages.indexOf(split.pages[0]); + e.returnValue = pages.indexOf(split.pairs[0].page); else e.returnValue = idx; }, @@ -412,8 +421,8 @@ module.exports = ext.register("ext/splitview/splitview", { var splitEl; for (i = 0, l = splits.length; i < l; ++i) { splitEl = apf.getXml(""); - splitEl.setAttribute("pages", splits[i].pages.map(function(page) { - return page.id; + splitEl.setAttribute("pages", splits[i].pairs.map(function(pair) { + return pair.page.id; }).join(",")); splitEl.setAttribute("active", Splits.isActive(splits[i]) ? "true" : "false"); splitEl.setAttribute("layout", splits[i].gridLayout); From d63f8946ec3a14f0e09eadef05a003d87a681b56 Mon Sep 17 00:00:00 2001 From: mikedeboer Date: Mon, 16 Jan 2012 13:10:21 +0100 Subject: [PATCH 27/69] updated apf --- client/js/apf_release.js | 5 +++-- support/apf | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/client/js/apf_release.js b/client/js/apf_release.js index 0459c830cfc..60b2e9c5d46 100644 --- a/client/js/apf_release.js +++ b/client/js/apf_release.js @@ -6747,8 +6747,9 @@ delete _self.$btnControl[aml.$uniqueId];if(div&&div.parentNode){div.parentNode.i }_self.$lastPosition=_self.$lastLeft=undefined;}});}});apf.addListener(document,"mouseup",mUp=function(e){if(!e){e=event; }var aml=_self.$lastPosition!==null?apf.findHost(_self.$lastPosition||div.nextSibling):null; if(started&&aml!=_self.nextSibling){apf.tween.single(_self.$button,{steps:20,interval:10,from:_self.$button.offsetLeft,to:_self.$lastLeft||div.offsetLeft,type:"left",control:_self.$btnControl[_self.$uniqueId]={},anim:apf.tween.easeInOutCubic,onstop:function(){},onfinish:function(){oHtml.style.position=oHtml.style.zIndex=oHtml.style.top=oHtml.style.left=""; -_self.parentNode.insertBefore(_self,aml);div.parentNode.removeChild(div);delete _self.$btnControl[_self.$uniqueId]; -}});}else{oHtml.style.position=oHtml.style.zIndex=oHtml.style.top=oHtml.style.left=""; +var reorder=_self.nextSibling!=aml;_self.parentNode.insertBefore(_self,aml);div.parentNode.removeChild(div); +if(reorder){_self.parentNode.dispatchEvent("reorder",{page:_self.localName!="page"?_self.parentNode.$activepage:_self}); +}delete _self.$btnControl[_self.$uniqueId];}});}else{oHtml.style.position=oHtml.style.zIndex=oHtml.style.top=oHtml.style.left=""; div.parentNode.removeChild(div);}apf.removeListener(document,"mouseup",mUp);apf.removeListener(document,"mousemove",mMove); });}}};this.$btnUp=function(oHtml){this.parentNode.$setStyleClass(oHtml,"",["down"],true); if(this.disabled){return;}if(this.parentNode.$order&&this.$btnPressed){this.$dragging=false; diff --git a/support/apf b/support/apf index 7bc3c9a938b..d2711cbc174 160000 --- a/support/apf +++ b/support/apf @@ -1 +1 @@ -Subproject commit 7bc3c9a938b377061c0ccb1907fbd4114d0e168e +Subproject commit d2711cbc174d370abeb185297ff31ccdb1f163ea From 9b1b549f2b29630f419f90d44528ba4f1b40bda2 Mon Sep 17 00:00:00 2001 From: mikedeboer Date: Mon, 16 Jan 2012 13:10:57 +0100 Subject: [PATCH 28/69] fixed restore from settings and reorder on page dragdrop --- client/ext/splitview/splitview.js | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/client/ext/splitview/splitview.js b/client/ext/splitview/splitview.js index ddc576d84d0..0020c6dfe97 100644 --- a/client/ext/splitview/splitview.js +++ b/client/ext/splitview/splitview.js @@ -106,19 +106,21 @@ module.exports = ext.register("ext/splitview/splitview", { }); tabs.addEventListener("reorder", function(e) { - var split = Splits.get(e.page); - console.log("got split",split,Splits.getActive()) - if (Splits.isActive(split)) { - console.log("UPDATING!!"); - Splits.show(split); - } + Splits.get(e.page).forEach(function(split) { + if (Splits.isActive(split)) + Splits.update(split); + }); + _self.save(); }); ide.addEventListener("loadsettings", function(e) { if (!e.model || !e.model.data) return; - setTimeout(function() { - _self.restore(e.model.data); + var data = e.model.data; + ide.addEventListener("extload", function(){ + setTimeout(function() { + _self.restore(data); + }, 50); }); }); @@ -435,7 +437,7 @@ module.exports = ext.register("ext/splitview/splitview", { restore: function(settings) { // no tabs open... don't bother ;) var tabs = tabEditors; - if (tabs.childNodes.length <= 1) + if (tabs.getPages().length <= 1) return; var nodes = settings.selectNodes("splits/split"); From eed41dd9dc6f6684c145e6ff20db63eebd1285cd Mon Sep 17 00:00:00 2001 From: mikedeboer Date: Mon, 16 Jan 2012 13:49:13 +0100 Subject: [PATCH 29/69] added moving tabs with the keyboard to tabbehaviors and remapped merge tab keybindings of splitview --- client/ext/keybindings_default/default_mac.js | 6 +- client/ext/keybindings_default/default_win.js | 6 +- client/ext/tabbehaviors/tabbehaviors.js | 55 +++++++++++++++++-- 3 files changed, 57 insertions(+), 10 deletions(-) diff --git a/client/ext/keybindings_default/default_mac.js b/client/ext/keybindings_default/default_mac.js index 5db4f1b7609..6cfd193211f 100644 --- a/client/ext/keybindings_default/default_mac.js +++ b/client/ext/keybindings_default/default_mac.js @@ -80,6 +80,8 @@ return keys.onLoad({ "closeallbutme": "Command-Option-W", "gototabright": "Command-]", "gototableft": "Command-[", + "movetableft": "Command-Option-[", + "movetabright": "Command-Option-]", "tab1": "Command-1", "tab2": "Command-2", "tab3": "Command-3", @@ -98,8 +100,8 @@ return keys.onLoad({ "savetabsession": "Command-Alt-S" }, "splitview" : { - "mergetableft": "Command-Option-[", - "mergetabright": "Command-Option-]" + "mergetableft": "Command-Option-Ctrl-[", + "mergetabright": "Command-Option-Ctrl-]" }, "code" : { "selectall": "Command-A", diff --git a/client/ext/keybindings_default/default_win.js b/client/ext/keybindings_default/default_win.js index bd3ffefc163..500a429e586 100644 --- a/client/ext/keybindings_default/default_win.js +++ b/client/ext/keybindings_default/default_win.js @@ -81,6 +81,8 @@ return keys.onLoad({ "closeallbutme": "Ctrl-Alt-W", "gototabright": "Ctrl-]", "gototableft": "Ctrl-[", + "movetableft": "Ctrl-Alt-[", + "movetabright": "Ctrl-Alt-]", "tab1": "Ctrl-1", "tab2": "Ctrl-2", "tab3": "Ctrl-3", @@ -99,8 +101,8 @@ return keys.onLoad({ "savetabsession": "Ctrl-Alt-S" }, "splitview" : { - "mergetableft": "Ctrl-Alt-[", - "mergetabright": "Ctrl-Alt-]" + "mergetableft": "Ctrl-Alt-Meta-[", + "mergetabright": "Ctrl-Alt-Meta-]" }, "code" : { "selectall": "Ctrl-A", diff --git a/client/ext/tabbehaviors/tabbehaviors.js b/client/ext/tabbehaviors/tabbehaviors.js index b2b5bfed31f..ab24b52ee7f 100644 --- a/client/ext/tabbehaviors/tabbehaviors.js +++ b/client/ext/tabbehaviors/tabbehaviors.js @@ -31,6 +31,8 @@ module.exports = ext.register("ext/tabbehaviors/tabbehaviors", { "closeallbutme": {hint: "close all opened tabs, but the tab that is currently active", msg: "Closing tabs."}, "gototabright": {hint: "navigate to the next tab, right to the tab that is currently active", msg: "Switching to right tab."}, "gototableft": {hint: "navigate to the next tab, left to the tab that is currently active", msg: "Switching to left tab."}, + "movetabright": {hint: "move the tab that is currently active to the left", msg: "Moving tab to the left."}, + "movetableft": {hint: "move the tab that is currently active to the left", msg: "Moving tab to the left."}, "tab1": {hint: "navigate to the first tab", msg: "Switching to tab 1."}, "tab2": {hint: "navigate to the second tab", msg: "Switching to tab 2."}, "tab3": {hint: "navigate to the third tab", msg: "Switching to tab 3."}, @@ -321,17 +323,17 @@ module.exports = ext.register("ext/tabbehaviors/tabbehaviors", { }, cycleTab: function(dir) { - var bRight = dir == "right", - tabs = tabEditors, - pages = tabs.getPages(), - curr = tabs.getPage(), - currIdx = pages.indexOf(curr); + var bRight = dir == "right"; + var tabs = tabEditors; + var pages = tabs.getPages(); + var curr = tabs.getPage(); + var currIdx = pages.indexOf(curr); if (!curr || pages.length == 1) return; var idx = currIdx + (bRight ? 1 : -1); if (idx < 0) idx = pages.length - 1; - if (idx > pages.length -1) + if (idx > pages.length - 1) idx = 0; // other plugins may modify this behavior @@ -348,6 +350,47 @@ module.exports = ext.register("ext/tabbehaviors/tabbehaviors", { tabs.set(pages[idx].id); return false; }, + + movetabright: function() { + this.moveTab("right"); + }, + + movetableft: function() { + this.moveTab("left"); + }, + + moveTab: function(dir) { + var bRight = dir == "right"; + var tabs = tabEditors; + var pages = tabs.getPages(); + var curr = tabs.getPage(); + var currIdx = pages.indexOf(curr); + var append = false; + if (!curr || pages.length == 1) + return; + var idx = currIdx + (bRight ? 2 : -1); + if (idx < 0 || idx === pages.length) + append = true; + if (idx > pages.length - 1) + idx = 0; + + // other plugins may modify this behavior + var res = ide.dispatchEvent("beforemovetab", { + index: idx, + dir: dir, + pages: pages + }); + if (res === false) + return; + if (typeof res == "number") + idx = res; + + if (append) + tabs.appendChild(curr) + else + tabs.insertBefore(curr, pages[idx]); + return false; + }, tab1: function() {return this.showTab(1);}, tab2: function() {return this.showTab(2);}, From f08889e4c04f55ae9d42352e755ce7471fff6261 Mon Sep 17 00:00:00 2001 From: mikedeboer Date: Mon, 16 Jan 2012 15:50:46 +0100 Subject: [PATCH 30/69] added zManager to neatly order editors to make popover dialog anims look better --- client/ext/gotoline/gotoline.js | 16 ++++++--- client/ext/quicksearch/quicksearch.js | 18 +++++++--- client/ext/splitview/splits.js | 49 ++++++++++++--------------- client/ext/splitview/zmanager.js | 44 ++++++++++++++++++++++++ 4 files changed, 92 insertions(+), 35 deletions(-) create mode 100644 client/ext/splitview/zmanager.js diff --git a/client/ext/gotoline/gotoline.js b/client/ext/gotoline/gotoline.js index 875ca02d466..1e94eed3ff4 100644 --- a/client/ext/gotoline/gotoline.js +++ b/client/ext/gotoline/gotoline.js @@ -141,7 +141,8 @@ module.exports = ext.register("ext/gotoline/gotoline", { var epos = apf.getAbsolutePosition(aceHtml); var maxTop = aceHtml.offsetHeight - 100; var corrected = ide.dispatchEvent("ext.gotoline.correctpos", { - pos: pos + pos: pos, + anim: "out" }); //editor.amlEditor.parentNode.appendChild(winGotoLine); @@ -155,26 +156,33 @@ module.exports = ext.register("ext/gotoline/gotoline", { if (corrected) { if (typeof corrected.top != "undefined") winGotoLine.$ext.style.top = corrected.top + "px"; + winGotoLine.$ext.style.zIndex = corrected.zIndex || winGotoLine.zindex; + } + else { + winGotoLine.$ext.style.zIndex = winGotoLine.zindex; } //Animate apf.tween.single(winGotoLine, { type : "left", anim : apf.tween.easeInOutCubic, - from : -60, - to : (corrected && typeof corrected.left != "undefined") ? corrected.left : 0, + from : corrected ? corrected.from : -60, + to : corrected ? corrected.to : 0, steps : 8, interval : 10, control : (this.control = {}) }); } else if (winGotoLine.visible) { + var corrected = ide.dispatchEvent("ext.gotoline.correctpos", { + anim: "in" + }); //Animate apf.tween.single(winGotoLine, { type : "left", anim : apf.tween.EASEOUT, from : winGotoLine.$ext.offsetLeft, - to : -60, + to : corrected ? corrected.to : -60, steps : 8, interval : 10, control : (this.control = {}), diff --git a/client/ext/quicksearch/quicksearch.js b/client/ext/quicksearch/quicksearch.js index a319154a315..aac6beb2a85 100644 --- a/client/ext/quicksearch/quicksearch.js +++ b/client/ext/quicksearch/quicksearch.js @@ -205,7 +205,9 @@ module.exports = ext.register("ext/quicksearch/quicksearch", { var doc = editor.getDocument(); var range = sel.getRange(); var value = doc.getTextRange(range); - var corrected = ide.dispatchEvent("ext.quicksearch.correctpos"); + var corrected = ide.dispatchEvent("ext.quicksearch.correctpos", { + anim: "out" + }); if (!value && editor.amlEditor) value = editor.amlEditor.getLastSearchOptions().needle; @@ -221,14 +223,18 @@ module.exports = ext.register("ext/quicksearch/quicksearch", { if (corrected) { if (typeof corrected.right != "undefined") winQuickSearch.$ext.style.right = corrected.right + "px"; + winQuickSearch.$ext.style.zIndex = corrected.zIndex || winQuickSearch.zindex; + } + else { + winQuickSearch.$ext.style.zIndex = winQuickSearch.zindex; } //Animate apf.tween.single(winQuickSearch, { type : "top", anim : apf.tween.easeInOutCubic, - from : -27, - to : (corrected && typeof corrected.top != "undefined") ? corrected.top : 2, + from : corrected ? corrected.from : -27, + to : corrected ? corrected.to : 2, steps : 8, interval : 10, control : (this.control = {}), @@ -238,6 +244,10 @@ module.exports = ext.register("ext/quicksearch/quicksearch", { }); } else if (winQuickSearch.visible) { + var corrected = ide.dispatchEvent("ext.quicksearch.correctpos", { + anim: "in" + }); + txtQuickSearch.focus(); txtQuickSearch.select(); @@ -246,7 +256,7 @@ module.exports = ext.register("ext/quicksearch/quicksearch", { type : "top", anim : apf.tween.NORMAL, from : winQuickSearch.$ext.offsetTop, - to : -30, + to : corrected ? corrected.to : -30, steps : 8, interval : 10, control : (this.control = {}), diff --git a/client/ext/splitview/splits.js b/client/ext/splitview/splits.js index 12b916cc36b..51fd8c9c39c 100644 --- a/client/ext/splitview/splits.js +++ b/client/ext/splitview/splits.js @@ -10,6 +10,7 @@ define(function(require, exports, module) { var ide = require("core/ide"); var Grids = require("ext/splitview/grids"); +var ZManager = require("ext/splitview/zmanager"); var Editors = require("ext/editors/editors"); var HashHandler = require("ace/keyboard/hash_handler").HashHandler; var Splits = []; @@ -46,7 +47,7 @@ exports.init = function(splitView) { }); ide.addEventListener("ext.quicksearch.correctpos", function(e) { - e.returnValue = correctQuickSearchDialog(); + e.returnValue = correctQuickSearchDialog(e); }); ide.addEventListener("ext.gotoline.correctpos", function(e) { @@ -95,7 +96,8 @@ exports.create = function(page, gridLayout) { editor: editor }], activePage: 0, - gridLayout: gridLayout + gridLayout: gridLayout, + zManager: new ZManager() }; Splits.push(split); @@ -177,6 +179,8 @@ exports.update = function(split, gridLayout) { amlPage.appendChild(editor); editor.show(); + split.zManager.clear(editor.$ext); + clearSplitViewStyles(page); Grids.hide(split.gridLayout); @@ -197,6 +201,9 @@ exports.update = function(split, gridLayout) { Grids.update(gridLayout, split); // make sure visual styles are OK setSplitViewStyles(split); + split.zManager.resetAll(split.pairs.map(function(pair) { + return pair.editor.$ext; + }).reverse()); exports.setActivePage(split); @@ -232,6 +239,7 @@ exports.mutate = function(split, page) { page.$deactivateButton(); clearSplitViewStyles(page); editor.hide(); + split.zManager.clear(editor.$ext); if (tabEditors.getPage() !== split.pairs[0].page) tabEditors.set(split.pairs[0].page); @@ -274,6 +282,7 @@ exports.mutate = function(split, page) { }); //console.log("setting model of ", editorToUse.id, "to", page.$model.data.xml); exports.consolidateEditorSession(page, editorToUse); + split.zManager.set(editorToUse.$ext); this.show(split); } @@ -333,31 +342,12 @@ exports.indexOf = function(split, obj) { } function sortEditorsAndPages(split) { - // lstOpenFiles.$model.data.selectNodes("//file") var pages = tabEditors.getPages(); - var p = []; - var e = []; - var index; split.pairs.sort(function(pair1, pair2) { var idx1 = pages.indexOf(pair1.page); var idx2 = pages.indexOf(pair2.page); return idx1 > idx2 ? 1 : idx1 < idx2 ? -1 : 0; }); - return; - //console.log("before sort: ", [].concat(split.pages).map(function(p) { return p.name; }), - // [].concat(split.editors).map(function(e) { return e.id; })); - for (var i = 0, c = 0, l = pages.length, l2 = split.pages.length; i < l && c < l2; ++i) { - if ((index = exports.indexOf(split, pages[i])) > -1) { - //console.log("pushing page at index " + i + " which is in the split at " - // + index + ", names " + pages[i].name + ", " + split.pages[index].name); - p.push(split.pages[index]); - e.push(split.editors[index]); - ++c; - } - } - //console.log("after sort:", p.map(function(p) { return p.name; }), e.map(function(e) { return e.id; })); - split.pages = p; - split.editors = e; } function createEditorClones(editor) { @@ -500,7 +490,7 @@ function setSplitViewStyles(splitOrPage) { var searchWindow, gotoLineWindow, searchPos; -function correctQuickSearchDialog() { +function correctQuickSearchDialog(e) { var editor = Editors.currentEditor.amlEditor; var pos = !ActiveSplit ? -1 : exports.indexOf(ActiveSplit, editor); if (pos == -1) @@ -527,9 +517,12 @@ function correctQuickSearchDialog() { var right = parentDims.width - editorPos[0] - editorDims.width + 30; var top = editorPos[1]; //console.log("editorPos", editorPos,"editorDims",JSON.stringify(editorDims),"parentDims",JSON.stringify(parentDims),"right",right,"top",top); + var to = Math.max(top, 0); return { right: Math.max(right, 30), - top: Math.max(top, 0) + zIndex: parseInt(editor.$ext.style.zIndex, 10) + 1, + from: !e || e.anim == "out" ? to - 27 : 0, + to: !e || e.anim == "out" ? to : (to - 30) }; } } @@ -551,13 +544,15 @@ function correctGotoLineDialog(e) { gotoLineWindow = self["winGotoLine"]; if (gotoLineWindow) { - var pos = e.pos; - var maxTop = editorPos[1] + editorDims.height - 100; var left = editorPos[0]; - var top = Math.min(maxTop, pos.pageY - 70); + var to = Math.max(left, 0); + var maxTop = editorPos[1] + editorDims.height - 100; + var top = e.pos ? Math.min(maxTop, e.pos.pageY - 70) : undefined; return { top: top, - left: Math.max(left, 0) + zIndex: parseInt(editor.$ext.style.zIndex, 10) + 1, + from: e.anim == "out" ? to - 60 : 0, + to: e.anim == "out" ? to : (to - 60) }; } } diff --git a/client/ext/splitview/zmanager.js b/client/ext/splitview/zmanager.js new file mode 100644 index 00000000000..9a0071cff4b --- /dev/null +++ b/client/ext/splitview/zmanager.js @@ -0,0 +1,44 @@ +/** + * Manage z-indexes + * + * @copyright 2010, Ajax.org B.V. + * @author Mike de Boer + * @license GPLv3 + */ + +define(function(require, exports, module) { + +module.exports = function(startIndex, increment) { + var base = startIndex || 1; + var incrBy = increment || 2; + + this.set = function(htmlNode) { + htmlNode.$storedZ = apf.getStyle(htmlNode, "zIndex"); + base += incrBy; + htmlNode.style.zIndex = base; + }; + + this.clear = function(htmlNode) { + if (typeof htmlNode.$storedZ == "undefined") + return; + htmlNode.style.zIndex = htmlNode.$storedZ; + delete htmlNode.$storedZ; + }; + + this.resetAll = function(nodes) { + if (!apf.isArray(nodes) || !nodes.length) + return; + + base = startIndex || 1; + + var i = 0; + var l = nodes.length; + for (; i < l; ++i) + this.clear(nodes[i]); + for (i = 0; i < l; ++i) + this.set(nodes[i]); + }; + +}; + +}); From a6009cd2429601574e780ad4674779fcb53530c9 Mon Sep 17 00:00:00 2001 From: mikedeboer Date: Mon, 16 Jan 2012 16:37:21 +0100 Subject: [PATCH 31/69] updated apf --- client/js/apf_release.js | 5 +++-- support/apf | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/client/js/apf_release.js b/client/js/apf_release.js index 60b2e9c5d46..fefcbc5ca2e 100644 --- a/client/js/apf_release.js +++ b/client/js/apf_release.js @@ -414,8 +414,9 @@ this.$keys={};var _self=this,trace=0;function register(hotkey,handler,remove){va for(;i46&&code!=91?String.fromCharCode(code):null); +for(;i46&&code!=91?String.fromCharCode(code):null); if(!hashId&&(!key||!key.match(/^F\d{1,2}$/))||!key){return;}if(_self.$keys[hashId]&&(handler=_self.$keys[hashId][key.toLowerCase()])){handler(eInfo.htmlEvent); eInfo.returnValue=false;apf.queue.empty();}return eInfo.returnValue;};apf.removeHotkey=this.remove=this.unregister=function(hotkey,handler){var parts=hotkey.split("|"),i=0,l=parts.length; for(;i Date: Mon, 16 Jan 2012 16:37:44 +0100 Subject: [PATCH 32/69] fixed JS error in settings --- client/ext/settings/settings.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/ext/settings/settings.js b/client/ext/settings/settings.js index c88ebb9e06f..e2ec29d11e7 100644 --- a/client/ext/settings/settings.js +++ b/client/ext/settings/settings.js @@ -102,7 +102,7 @@ module.exports = ext.register("ext/settings/settings", { }, showsettings: function(e){ - panels.initPanel(this); + panels.activate(this); this.enable(); return false; }, From 75ddfb32097b98f9d738cd852900567c82b69948 Mon Sep 17 00:00:00 2001 From: mikedeboer Date: Mon, 16 Jan 2012 16:38:39 +0100 Subject: [PATCH 33/69] added hotkeys for navigating through editors in a split view --- client/ext/keybindings_default/default_mac.js | 4 ++- client/ext/keybindings_default/default_win.js | 4 ++- client/ext/splitview/splits.js | 14 +++++---- client/ext/splitview/splitview.js | 29 ++++++++++++++++++- 4 files changed, 42 insertions(+), 9 deletions(-) diff --git a/client/ext/keybindings_default/default_mac.js b/client/ext/keybindings_default/default_mac.js index 6cfd193211f..5518d8ee45c 100644 --- a/client/ext/keybindings_default/default_mac.js +++ b/client/ext/keybindings_default/default_mac.js @@ -101,7 +101,9 @@ return keys.onLoad({ }, "splitview" : { "mergetableft": "Command-Option-Ctrl-[", - "mergetabright": "Command-Option-Ctrl-]" + "mergetabright": "Command-Option-Ctrl-]", + "nexteditor": "Ctrl-]", + "preveditor": "Ctrl-[" }, "code" : { "selectall": "Command-A", diff --git a/client/ext/keybindings_default/default_win.js b/client/ext/keybindings_default/default_win.js index 500a429e586..edda97f8645 100644 --- a/client/ext/keybindings_default/default_win.js +++ b/client/ext/keybindings_default/default_win.js @@ -102,7 +102,9 @@ return keys.onLoad({ }, "splitview" : { "mergetableft": "Ctrl-Alt-Meta-[", - "mergetabright": "Ctrl-Alt-Meta-]" + "mergetabright": "Ctrl-Alt-Meta-]", + "nexteditor": "Ctrl-]", + "preveditor": "Ctrl-[" }, "code" : { "selectall": "Ctrl-A", diff --git a/client/ext/splitview/splits.js b/client/ext/splitview/splits.js index 51fd8c9c39c..1c01512960c 100644 --- a/client/ext/splitview/splits.js +++ b/client/ext/splitview/splits.js @@ -39,7 +39,7 @@ exports.init = function(splitView) { var config = previousEditor ? previousEditor.$editor.getKeyboardHandler() - : new HashHandler(bindings.code) + : new HashHandler(bindings.code); editor.$editor.setKeyboardHandler(config); }); } @@ -322,7 +322,9 @@ exports.getActive = function() { }; exports.setActivePage = function(split, activePage) { - var idx = activePage ? exports.indexOf(split, activePage) : split.activePage; + var idx = split.activePage = activePage + ? exports.indexOf(split, activePage) + : split.activePage; if (idx == -1) return; (split.pairs[idx] ? split.pairs[idx].editor : split.pairs[0].editor).focus(); @@ -339,7 +341,7 @@ exports.indexOf = function(split, obj) { return i; } return -1; -} +}; function sortEditorsAndPages(split) { var pages = tabEditors.getPages(); @@ -481,7 +483,7 @@ function setSplitViewStyles(splitOrPage) { for (var i = 0, l = pages.length; i < l; ++i) { if (!pages[i]) continue; - if (i == 0) + if (i === 0) apf.setStyleClass(pages[i].$button, null, [NPlusOneClass]); else apf.setStyleClass(pages[i].$button, NPlusOneClass, []); @@ -501,7 +503,7 @@ function correctQuickSearchDialog(e) { var editorDims = { width: editor.$ext.offsetWidth, height: editor.$ext.offsetHeight - } + }; var parentDims = { width: parent.$ext.offsetWidth, height: parent.$ext.offsetHeight @@ -538,7 +540,7 @@ function correctGotoLineDialog(e) { var editorDims = { width: editor.$ext.offsetWidth, height: editor.$ext.offsetHeight - } + }; if (!gotoLineWindow && self["winGotoLine"]) gotoLineWindow = self["winGotoLine"]; diff --git a/client/ext/splitview/splitview.js b/client/ext/splitview/splitview.js index 0020c6dfe97..5713eac5272 100644 --- a/client/ext/splitview/splitview.js +++ b/client/ext/splitview/splitview.js @@ -29,7 +29,9 @@ module.exports = ext.register("ext/splitview/splitview", { commands : { "mergetableft": {hint: "Add the page on the left of the currently active page to a split view"}, - "mergetabright": {hint: "Add the page on the right of the currently active page to a split view"} + "mergetabright": {hint: "Add the page on the right of the currently active page to a split view"}, + "nexteditor": {hint: "Navigate to the next editor right or below the editor that is currently active in the current split view"}, + "preveditor": {hint: "Navigate to the previous editor left or above the editor that is currently active in the current split view"} }, hotitems : [], nodes : [], @@ -170,6 +172,31 @@ module.exports = ext.register("ext/splitview/splitview", { return false; }, + nexteditor: function() { + this.cycleEditors("next"); + }, + + preveditor: function() { + this.cycleEditors("prev"); + }, + + cycleEditors: function(dir) { + var split = Splits.getActive(); + if (!split) + return; + + var bNext = dir == "next"; + var currIdx = split.activePage; + var idx = currIdx + (bNext ? 1 : -1); + if (idx < 0) + idx = split.pairs.length - 1; + if (idx > split.pairs.length - 1) + idx = 0; + + Splits.setActivePage(split, split.pairs[idx].page); + return false; + }, + /** * Invoked when a file is closed * From 61821463348c4ed9376a8367c75f889fb755c8bc Mon Sep 17 00:00:00 2001 From: mikedeboer Date: Mon, 16 Jan 2012 17:17:38 +0100 Subject: [PATCH 34/69] run panel updates to active file in split view. fixes #696 --- client/ext/runpanel/runpanel.js | 26 ++++++++++++-------------- client/ext/splitview/splits.js | 8 +++++++- 2 files changed, 19 insertions(+), 15 deletions(-) diff --git a/client/ext/runpanel/runpanel.js b/client/ext/runpanel/runpanel.js index 3a58d57399e..c9526f3c733 100644 --- a/client/ext/runpanel/runpanel.js +++ b/client/ext/runpanel/runpanel.js @@ -119,30 +119,28 @@ module.exports = ext.register("ext/runpanel/runpanel", { mdlRunConfigurations.load(runConfigs); }); - var page = tabEditors.getPage(); - if (page) { + function pageSwitch(page) { + if (!page) + return; + var path = page.$model.queryValue("@path").replace(ide.davPrefix, ""); mdlRunConfigurations.setQueryValue("config[@curfile]/@path", path); mdlRunConfigurations.setQueryValue("config[@curfile]/@name", path.split("/").pop() + " (active file)"); } + pageSwitch(tabEditors.getPage()); + tabEditors.addEventListener("afterswitch", function(e){ - var page = e.nextPage; - var path = page.$model.queryValue("@path").replace(ide.davPrefix, ""); - mdlRunConfigurations.setQueryValue("config[@curfile]/@path", path); - mdlRunConfigurations.setQueryValue("config[@curfile]/@name", - path.split("/").pop() + " (active file)"); + pageSwitch(e.nextPage); }); ide.addEventListener("afterfilesave", function(e){ - var page = tabEditors.getPage(); - if (page) { - var path = page.$model.queryValue("@path").replace(ide.davPrefix, ""); - mdlRunConfigurations.setQueryValue("config[@curfile]/@path", path); - mdlRunConfigurations.setQueryValue("config[@curfile]/@name", - path.split("/").pop() + " (active file)"); - } + pageSwitch(tabEditors.getPage()); + }); + + ide.addEventListener("pageswitch", function(e) { + pageSwitch(e.page); }); var hasBreaked = false; diff --git a/client/ext/splitview/splits.js b/client/ext/splitview/splits.js index 1c01512960c..4f9590654d0 100644 --- a/client/ext/splitview/splits.js +++ b/client/ext/splitview/splits.js @@ -322,12 +322,16 @@ exports.getActive = function() { }; exports.setActivePage = function(split, activePage) { + var old = split.activePage; var idx = split.activePage = activePage ? exports.indexOf(split, activePage) : split.activePage; if (idx == -1) return; - (split.pairs[idx] ? split.pairs[idx].editor : split.pairs[0].editor).focus(); + var pair = split.pairs[idx] ? split.pairs[idx] : split.pairs[0]; + pair.editor.focus(); + if (idx !== old) + ide.dispatchEvent("pageswitch", { page: pair.page }); }; /* @@ -458,6 +462,8 @@ function onEditorFocus(editor) { for (var page, i = 0, l = split.pairs.length; i < l; ++i) { page = split.pairs[i].page; if (page === activePage) { + if (split.activePage !== i) + ide.dispatchEvent("pageswitch", { page: page }); split.activePage = i; apf.setStyleClass(page.$button, ActiveClass, [InactiveClass]); } From fa762ef00f070e7eae38ed688516d20a6988a1f5 Mon Sep 17 00:00:00 2001 From: mikedeboer Date: Tue, 17 Jan 2012 12:14:13 +0100 Subject: [PATCH 35/69] fixed quicksearch and gotoline window positioning. fixes #773 and #774 --- client/ext/quicksearch/quicksearch.js | 2 ++ client/ext/splitview/grids.js | 1 + client/ext/splitview/splits.js | 7 +++++-- 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/client/ext/quicksearch/quicksearch.js b/client/ext/quicksearch/quicksearch.js index aac6beb2a85..6fe3ef74575 100644 --- a/client/ext/quicksearch/quicksearch.js +++ b/client/ext/quicksearch/quicksearch.js @@ -240,6 +240,8 @@ module.exports = ext.register("ext/quicksearch/quicksearch", { control : (this.control = {}), onfinish : function() { _self.updateCounter(); + if (corrected && corrected.onfinish) + corrected.onfinish(); } }); } diff --git a/client/ext/splitview/grids.js b/client/ext/splitview/grids.js index 76dbb95db1a..f328a550054 100644 --- a/client/ext/splitview/grids.js +++ b/client/ext/splitview/grids.js @@ -219,6 +219,7 @@ function createNodes(struct, splitters, parent) { else if (nodeName == "splitter") { options.visible = false; options.skin = "darksplitter"; + options.zindex = 1; } var node = parent.appendChild(new apf[nodeName](options)); diff --git a/client/ext/splitview/splits.js b/client/ext/splitview/splits.js index 4f9590654d0..146d1685fc3 100644 --- a/client/ext/splitview/splits.js +++ b/client/ext/splitview/splits.js @@ -530,7 +530,10 @@ function correctQuickSearchDialog(e) { right: Math.max(right, 30), zIndex: parseInt(editor.$ext.style.zIndex, 10) + 1, from: !e || e.anim == "out" ? to - 27 : 0, - to: !e || e.anim == "out" ? to : (to - 30) + to: !e || e.anim == "out" ? to : (to - 30), + onfinish: function() { + searchWindow.$ext.style.zIndex = searchWindow.zindex; + } }; } } @@ -560,7 +563,7 @@ function correctGotoLineDialog(e) { top: top, zIndex: parseInt(editor.$ext.style.zIndex, 10) + 1, from: e.anim == "out" ? to - 60 : 0, - to: e.anim == "out" ? to : (to - 60) + to: e.anim == "out" ? to + (to === 0 ? 2 : -1) : (to - 60) }; } } From 2f3df08a42beb7a51d9a5c96dfdab400e959f302 Mon Sep 17 00:00:00 2001 From: mikedeboer Date: Tue, 17 Jan 2012 15:10:06 +0100 Subject: [PATCH 36/69] fixed vim mode in split view, changing of syntax highlighting in general AND splitview, splits restore speed. Fixes #776, fixes #778, fixes #780 --- client/core/ide.js | 8 ++++- client/ext/code/code.js | 5 +-- client/ext/splitview/splits.js | 52 +++++++++++++++++++++++-------- client/ext/splitview/splitview.js | 11 ++++++- client/ext/vim/maps/util.js | 14 +++++---- client/ext/vim/vim.js | 44 +++++++++++++++++--------- 6 files changed, 97 insertions(+), 37 deletions(-) diff --git a/client/core/ide.js b/client/core/ide.js index 32f8e1b80cd..7474c45a989 100644 --- a/client/core/ide.js +++ b/client/core/ide.js @@ -266,7 +266,13 @@ define(function(require, exports, module) { if (!page) return null; - return page.$model.data; + var corrected = this.dispatchEvent("activepagemodel", { + model: page.$model + }); + + return corrected && corrected.data + ? corrected.data + : page.$model.data; }; ide.getAllPageModels = function() { diff --git a/client/ext/code/code.js b/client/ext/code/code.js index 5a275f34f3e..088b2af46b5 100644 --- a/client/ext/code/code.js +++ b/client/ext/code/code.js @@ -468,8 +468,9 @@ module.exports = ext.register("ext/code/code", { if (typeof ext === "string") { node = settings.model.queryNode('auto/customtypes/mime[@ext="' + ext + '"]'); if (!node) - settings.model.appendXml('', "auto/customtypes"); - } else { + node = settings.model.appendXml('', "auto/customtypes"); + } + else { var name = ext.getAttribute("name") || ""; node = settings.model.queryNode('auto/customtypes/mime[@filename="' + name + '"]'); if (node) diff --git a/client/ext/splitview/splits.js b/client/ext/splitview/splits.js index 146d1685fc3..c45de9e7f91 100644 --- a/client/ext/splitview/splits.js +++ b/client/ext/splitview/splits.js @@ -54,6 +54,10 @@ exports.init = function(splitView) { e.returnValue = correctGotoLineDialog(e); }); + ide.addEventListener("ext.vim.toggle", function(e) { + e.returnValue = correctVimMode(e); + }); + Grids.addEventListener("resize", function(e, node) { var correct; if (searchWindow && searchWindow.visible) { @@ -166,9 +170,9 @@ exports.update = function(split, gridLayout) { // split.editors.length,page.name,split.pages.map(function(page){return page.name})); if (split.pairs.length === 1) { var editor = page.$editor.amlEditor; - if (EditorClones[editor.tagName]) { - for (var clone, i = 0, l = EditorClones[editor.tagName].length; i < l; ++i) { - clone = EditorClones[editor.tagName][i]; + if (EditorClones[editor.localName]) { + for (var clone, i = 0, l = EditorClones[editor.localName].length; i < l; ++i) { + clone = EditorClones[editor.localName][i]; clone.hide(); apf.document.body.appendChild(clone); } @@ -218,7 +222,8 @@ exports.update = function(split, gridLayout) { exports.mutate = function(split, page) { split = split || split === null ? ActiveSplit : null; - var activePage = tabEditors.getPage(); + var tabs = tabEditors; + var activePage = tabs.getPage(); var pairIdx = split ? exports.indexOf(split, page) : -1; // Remove an editor from the split view @@ -240,8 +245,8 @@ exports.mutate = function(split, page) { clearSplitViewStyles(page); editor.hide(); split.zManager.clear(editor.$ext); - if (tabEditors.getPage() !== split.pairs[0].page) - tabEditors.set(split.pairs[0].page); + if (tabs.getPage() !== split.pairs[0].page) + tabs.set(split.pairs[0].page); this.update(split); } @@ -321,10 +326,10 @@ exports.getActive = function() { return ActiveSplit || null; }; -exports.setActivePage = function(split, activePage) { +exports.setActivePage = function(split, page) { var old = split.activePage; - var idx = split.activePage = activePage - ? exports.indexOf(split, activePage) + var idx = split.activePage = page + ? exports.indexOf(split, page) : split.activePage; if (idx == -1) return; @@ -339,7 +344,7 @@ exports.setActivePage = function(split, activePage) { * instead of '==='! */ exports.indexOf = function(split, obj) { - var type = obj.tagName.indexOf("page") > -1 ? "page" : "editor"; + var type = obj.localName.indexOf("page") > -1 ? "page" : "editor"; for (var i = 0, l = split.pairs.length; i < l; ++i) { if (split.pairs[i][type] === obj) return i; @@ -357,7 +362,7 @@ function sortEditorsAndPages(split) { } function createEditorClones(editor) { - var id = editor.tagName; + var id = editor.localName; var isCodeEditor = id.indexOf("codeeditor") > -1; if (!EditorClones.cloneEditor && isCodeEditor) { @@ -369,6 +374,21 @@ function createEditorClones(editor) { apf.document.body.appendChild(EditorClones.cloneEditor); addEditorListeners.call(this, EditorClones.cloneEditor); + + // add listeners to ceEditor properties that also need to be applied to + // other editor instances: + function setProp(which, value) { + if (EditorClones[id] && EditorClones[id].length) { + EditorClones[id].forEach(function(o) { + o.setAttribute(which, value); + }); + } + if (EditorClones.cloneEditor) + EditorClones.cloneEditor.setAttribute(which, value); + } + editor.addEventListener("prop.wrapmode", function(e) { + setProp("wrapmode", e.value); + }); } if (EditorClones[id] && EditorClones[id].length) { @@ -474,7 +494,7 @@ function onEditorFocus(editor) { } function clearSplitViewStyles(splitOrPage) { - var pages = (typeof splitOrPage.tagName != "undefined") + var pages = (typeof splitOrPage.localName != "undefined") ? [splitOrPage] : splitOrPage.pairs.map(function(pair) { return pair.page; }); pages.forEach(function(page) { @@ -483,7 +503,7 @@ function clearSplitViewStyles(splitOrPage) { } function setSplitViewStyles(splitOrPage) { - var pages = (typeof splitOrPage.tagName != "undefined") + var pages = (typeof splitOrPage.localName != "undefined") ? [null, splitOrPage] : splitOrPage.pairs.map(function(pair) { return pair.page; }); for (var i = 0, l = pages.length; i < l; ++i) { @@ -568,4 +588,10 @@ function correctGotoLineDialog(e) { } } +function correctVimMode(e) { + e.editors.push.apply(e.editors, EditorClones[e.editors[0].localName]); + if (EditorClones.cloneEditor) + e.editors.push(EditorClones.cloneEditor); +} + }); \ No newline at end of file diff --git a/client/ext/splitview/splitview.js b/client/ext/splitview/splitview.js index 5713eac5272..7fc31173495 100644 --- a/client/ext/splitview/splitview.js +++ b/client/ext/splitview/splitview.js @@ -122,10 +122,19 @@ module.exports = ext.register("ext/splitview/splitview", { ide.addEventListener("extload", function(){ setTimeout(function() { _self.restore(data); - }, 50); + }); }); }); + ide.addEventListener("activepagemodel", function(e) { + var page = tabEditors.getPage(); + var split = Splits.get(page)[0]; + if (!split || !Splits.isActive(split)) + return; + + e.returnValue = split.pairs[split.activePage || 0].page.$model; + }); + Splits.init(this); }, diff --git a/client/ext/vim/maps/util.js b/client/ext/vim/maps/util.js index 8322cefd7ed..2feabcc561c 100644 --- a/client/ext/vim/maps/util.js +++ b/client/ext/vim/maps/util.js @@ -54,12 +54,14 @@ module.exports = { editor.setStyle('normal-mode'); editor.clearSelection(); - var cursor = document.getElementsByClassName("ace_cursor")[0]; - if (cursor) { - cursor.style.display = null; - cursor.style.backgroundColor = "red"; - cursor.style.opacity = ".5"; - cursor.style.border = "0"; + var cursors = document.getElementsByClassName("ace_cursor"); + if (cursors && cursors.length) { + for (var i = 0, l = cursors.length; i < l; ++i) { + cursors[i].style.display = null; + cursors[i].style.backgroundColor = "red"; + cursors[i].style.opacity = ".5"; + cursors[i].style.border = "0"; + } } var pos; diff --git a/client/ext/vim/vim.js b/client/ext/vim/vim.js index 0ac0ac42afd..9100f4455d0 100644 --- a/client/ext/vim/vim.js +++ b/client/ext/vim/vim.js @@ -12,7 +12,7 @@ define(function(require, exports, module) { var ide = require("core/ide"); var ext = require("core/ext"); -var editors = require("ext/editors/editors"); +var Editors = require("ext/editors/editors"); var handler = require("ext/vim/keyboard").handler; var extSettings = require("ext/settings/settings"); var cmdModule = require("ext/vim/commands"); @@ -25,10 +25,10 @@ var OLD_HANDLER; var onConsoleCommand = function onConsoleCommand(e) { var cmd = e.data.command; - if (editors && editors.currentEditor && editors.currentEditor.amlEditor && + if (Editors && Editors.currentEditor && Editors.currentEditor.amlEditor && cmd && typeof cmd === "string") { - var domEditor = editors.currentEditor.amlEditor; + var domEditor = Editors.currentEditor.amlEditor; if (cmd[0] === ":") { cmd = cmd.substr(1); @@ -82,10 +82,17 @@ var onCursorMove = function() { }; var enableVim = function enableVim() { - if (editors.currentEditor && editors.currentEditor.amlEditor) { - ext.initExtension(this); + if (!(Editors.currentEditor && Editors.currentEditor.amlEditor)) + return; + var editors = [Editors.currentEditor.amlEditor]; + ide.dispatchEvent("ext.vim.toggle", { + editors: editors, + enable: true + }); - var editor = editors.currentEditor.amlEditor.$editor; + ext.initExtension(this); + editors.forEach(function(amlEditor) { + var editor = amlEditor.$editor; addCommands(editor, commands); editor.renderer.container.addEventListener("click", onCursorMove, false); @@ -95,33 +102,42 @@ var enableVim = function enableVim() { // Set Vim's own keyboard handle editor.setKeyboardHandler(handler); - if (util.currentMode !== "insert") { + if (util.currentMode !== "insert") commands.stop.exec(editor); - } + VIM_ENABLED = true; ide.dispatchEvent("track_action", {type: "vim", action: "enable"}); - } + }); }; var disableVim = function() { - if (editors.currentEditor && editors.currentEditor.amlEditor) { - var editor = editors.currentEditor.amlEditor.$editor; + if (!(Editors.currentEditor && Editors.currentEditor.amlEditor)) + return; + var editors = [Editors.currentEditor.amlEditor]; + ide.dispatchEvent("ext.vim.toggle", { + editors: editors, + enable: true + }); + + editors.forEach(function(amlEditor) { + var editor = amlEditor.$editor; removeCommands(editor, commands); editor.setKeyboardHandler(OLD_HANDLER); commands.start.exec(editor); editor.renderer.container.removeEventListener("click", onCursorMove, false); + VIM_ENABLED = false; ide.dispatchEvent("track_action", {type: "vim", action: "disable"}); - } + }); }; var cliKeyDown = function(e) { if (e.keyCode === 27) { // ESC is pressed in the CLI txtConsoleInput.blur(); - editors.currentEditor.amlEditor.focus(); + Editors.currentEditor.amlEditor.focus(); } }; @@ -129,7 +145,7 @@ module.exports = ext.register("ext/vim/vim", { name : "Vim mode", dev : "Ajax.org", type : ext.GENERAL, - deps : [editors], + deps : [Editors], nodes : [], alone : true, From a52a0e6c991e04b7f5efe6670dc80ddecb78540e Mon Sep 17 00:00:00 2001 From: mikedeboer Date: Wed, 18 Jan 2012 10:22:51 +0100 Subject: [PATCH 37/69] added missing style updates for Vim mode fix for split views --- client/ext/vim/maps/util.js | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/client/ext/vim/maps/util.js b/client/ext/vim/maps/util.js index 2feabcc561c..27d95f5bb19 100644 --- a/client/ext/vim/maps/util.js +++ b/client/ext/vim/maps/util.js @@ -18,15 +18,17 @@ module.exports = { // Switch editor to insert mode editor.unsetStyle('insert-mode'); - var cursor = document.getElementsByClassName("ace_cursor")[0]; - if (cursor) { - cursor.style.display = null; - cursor.style.backgroundColor = null; - cursor.style.opacity = null; - cursor.style.border = null; - cursor.style.borderLeftColor = isDarkTheme? "#eeeeee" : "#333333"; - cursor.style.borderLeftStyle = "solid"; - cursor.style.borderLeftWidth = "2px"; + var cursors = document.getElementsByClassName("ace_cursor"); + if (cursors && cursors.length) { + for (var i = 0, l = cursors.length; i < l; ++i) { + cursors[i].style.display = null; + cursors[i].style.backgroundColor = null; + cursors[i].style.opacity = null; + cursors[i].style.border = null; + cursors[i].style.borderLeftColor = isDarkTheme? "#eeeeee" : "#333333"; + cursors[i].style.borderLeftStyle = "solid"; + cursors[i].style.borderLeftWidth = "2px"; + } } editor.setOverwrite(false); From d3e1caad7df16c97faeec86f07d831af674e8bc5 Mon Sep 17 00:00:00 2001 From: mikedeboer Date: Wed, 18 Jan 2012 10:53:40 +0100 Subject: [PATCH 38/69] fixed JS error in editors.js --- client/ext/editors/editors.js | 37 ++++++++++++++++++++--------------- 1 file changed, 21 insertions(+), 16 deletions(-) diff --git a/client/ext/editors/editors.js b/client/ext/editors/editors.js index dcd7098cdf5..2e97f3fa471 100644 --- a/client/ext/editors/editors.js +++ b/client/ext/editors/editors.js @@ -149,9 +149,11 @@ module.exports = ext.register("ext/editors/editors", { }); apf.document.body.appendChild(tab); + + var tabs = tabEditors; - tabEditors.$buttons.appendChild(btn.$ext); - tabEditors.addEventListener("DOMNodeInserted",function(e){ + tabs.$buttons.appendChild(btn.$ext); + tabs.addEventListener("DOMNodeInserted",function(e){ if (e.$isMoveWithinParent) { //record position in settings @@ -163,15 +165,15 @@ module.exports = ext.register("ext/editors/editors", { } if (e.relatedNode == this && e.currentTarget.localName == "page") { - tabEditors.appendChild(btn); - tabEditors.$buttons.appendChild(btn.$ext); + tabs.appendChild(btn); + tabs.$buttons.appendChild(btn.$ext); btn.$ext.style.position = ""; btn.$ext.style.right = ""; btn.$ext.style.top = ""; } }); - tabEditors.addEventListener("DOMNodeRemoved",function(e){ + tabs.addEventListener("DOMNodeRemoved",function(e){ if (e.relatedNode == this && this.getPages().length == 1) { btn.$ext.style.position = "absolute"; btn.$ext.style.right = "5px"; @@ -284,10 +286,11 @@ module.exports = ext.register("ext/editors/editors", { openEditor : function(doc, init, active) { var xmlNode = doc.getNode(); var filepath = xmlNode.getAttribute("path"); + var tabs = tabEditors; - var page = tabEditors.getPage(filepath); + var page = tabs.getPage(filepath); if (page) { - tabEditors.set(page); + tabs.set(page); return; } @@ -312,10 +315,10 @@ module.exports = ext.register("ext/editors/editors", { //Create Fake Page if (init) - tabEditors.setAttribute("buttons", "close"); + tabs.setAttribute("buttons", "close"); var model = new apf.model(); - var fake = tabEditors.add("{([@changed] == 1 ? '*' : '') + [@name]}", filepath, editor.path, null, function(page){ + var fake = tabs.add("{([@changed] == 1 ? '*' : '') + [@name]}", filepath, editor.path, null, function(page){ page.$at = new apf.actiontracker(); page.$doc = doc; doc.$page = page; @@ -330,7 +333,7 @@ module.exports = ext.register("ext/editors/editors", { }); if (init) - tabEditors.setAttribute("buttons", "close,scale,order"); + tabs.setAttribute("buttons", "close,scale,order"); doc.addEventListener("setnode", function(e) { fake.$model.load(e.node); @@ -343,7 +346,7 @@ module.exports = ext.register("ext/editors/editors", { return; //Set active page - tabEditors.set(filepath); + tabs.set(filepath); //if (editorPage.model != model) //this.beforeswitch({nextPage: fake}); @@ -450,8 +453,8 @@ module.exports = ext.register("ext/editors/editors", { }, beforeswitch : function(e) { - var page = e.nextPage, - editorPage = tabEditors.getPage(page.type); + var page = e.nextPage; + var editorPage = tabEditors.getPage(page.type); if (!editorPage) return; if (editorPage.model != page.$model) @@ -766,7 +769,9 @@ module.exports = ext.register("ext/editors/editors", { jump : function(fileEl, row, column, text, doc, page) { var path = fileEl.getAttribute("path"); - var hasData = page && (tabEditors.getPage(path) || { }).$doc ? true : false; + var tabs = tabEditors; + var hasData = page && (tabs.getPage(path) || { }).$doc ? true : false; + var _self = this; if (row !== undefined) { var editor = _self.currentEditor.amlEditor; @@ -781,7 +786,7 @@ module.exports = ext.register("ext/editors/editors", { }; if (hasData) { - tabEditors.set(path); + tabs.set(path); jumpTo(); } else @@ -800,7 +805,7 @@ module.exports = ext.register("ext/editors/editors", { doc: doc || ide.createDocument(fileEl) }); else - tabEditors.set(path); + tabs.set(path); }, enable : function(){ From 3835fbf0c2e78785f2e61dd005fd96cbe5db740a Mon Sep 17 00:00:00 2001 From: mikedeboer Date: Wed, 18 Jan 2012 10:54:24 +0100 Subject: [PATCH 39/69] fixed the Preview button in split view. Fixes #781 --- client/ext/html/html.js | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/client/ext/html/html.js b/client/ext/html/html.js index 1a9baa4203d..67715f49c50 100644 --- a/client/ext/html/html.js +++ b/client/ext/html/html.js @@ -10,6 +10,7 @@ define(function(require, exports, module) { var ide = require("core/ide"); var ext = require("core/ext"); var code = require("ext/code/code"); +var Editors = require("ext/editors/editors"); var markup = require("text!ext/html/html.xml"); var mimeTypes = [ @@ -31,7 +32,10 @@ module.exports = ext.register("ext/html/html", { hook : function(){ var _self = this; - tabEditors.addEventListener("afterswitch", function(e){ + var tabs = tabEditors; + + tabs.addEventListener("afterswitch", function(e){ + console.log("gettin here?"); if (e.nextPage) { /*var ext = e.nextPage.id.split(".").pop(); @@ -39,7 +43,6 @@ module.exports = ext.register("ext/html/html", { || ext == ".js" || ext == ".txt" || ext == ".xml") {*/ ext.initExtension(_self); - _self.page = e.nextPage; _self.enable(); /*} else { @@ -50,6 +53,15 @@ module.exports = ext.register("ext/html/html", { _self.disable(); } }); + + tabs.addEventListener("DOMNodeRemoved", function(){ + // use a timeout to wait for the 'close tab' code flow to end, + // the amount of pages will be correct by then. + setTimeout(function() { + if (!tabs.getPages().length) + _self.disable(); + }); + }); }, init : function() { @@ -68,7 +80,11 @@ module.exports = ext.register("ext/html/html", { }, onOpenPage : function() { - var file = this.page.$model.data; + var editor = Editors.currentEditor && Editors.currentEditor.amlEditor + ? Editors.currentEditor.amlEditor : null; + if (!editor) + return; + var file = editor.$model.data; window.open(location.protocol + "//" + location.host + file.getAttribute("path"), "_blank"); }, From 83b4c40ae54ed447959481f9943f0405119fdbb6 Mon Sep 17 00:00:00 2001 From: mikedeboer Date: Wed, 18 Jan 2012 10:55:02 +0100 Subject: [PATCH 40/69] removed console.log --- client/ext/html/html.js | 1 - 1 file changed, 1 deletion(-) diff --git a/client/ext/html/html.js b/client/ext/html/html.js index 67715f49c50..a85cb5c6742 100644 --- a/client/ext/html/html.js +++ b/client/ext/html/html.js @@ -35,7 +35,6 @@ module.exports = ext.register("ext/html/html", { var tabs = tabEditors; tabs.addEventListener("afterswitch", function(e){ - console.log("gettin here?"); if (e.nextPage) { /*var ext = e.nextPage.id.split(".").pop(); From 23ecf5e73156a9f702f294ef793a4ca5d43a4cb1 Mon Sep 17 00:00:00 2001 From: mikedeboer Date: Wed, 18 Jan 2012 15:10:41 +0100 Subject: [PATCH 41/69] Fixed Alt-tabbing icw split views. Fixes #699 --- client/ext/splitview/splitview.js | 13 +++++++++++++ client/ext/tabbehaviors/tabbehaviors.js | 16 +++++++++++----- 2 files changed, 24 insertions(+), 5 deletions(-) diff --git a/client/ext/splitview/splitview.js b/client/ext/splitview/splitview.js index 7fc31173495..c48bd1c5f1b 100644 --- a/client/ext/splitview/splitview.js +++ b/client/ext/splitview/splitview.js @@ -85,6 +85,19 @@ module.exports = ext.register("ext/splitview/splitview", { _self.onCycleTab(e); }); + function onAccessTabbing(e) { + var split = Splits.get(e.page)[0]; + return !Splits.isActive(split); + } + + ide.addEventListener("beforenexttab", function(e) { + return onAccessTabbing(e); + }); + + ide.addEventListener("beforeprevioustab", function(e) { + return onAccessTabbing(e); + }); + ide.addEventListener("correctactivepage", function(e) { var split = Splits.getActive(); var editor = Editors.currentEditor && Editors.currentEditor.amlEditor; diff --git a/client/ext/tabbehaviors/tabbehaviors.js b/client/ext/tabbehaviors/tabbehaviors.js index ab24b52ee7f..15fa52cadc0 100644 --- a/client/ext/tabbehaviors/tabbehaviors.js +++ b/client/ext/tabbehaviors/tabbehaviors.js @@ -293,11 +293,14 @@ module.exports = ext.register("ext/tabbehaviors/tabbehaviors", { this.$tabAccessCycle = 2; } + var tabs = tabEditors; var next = this.accessed[n]; - if (next == tabEditors.getPage()) + if (next == tabs.getPage() || ide.dispatchEvent("beforenexttab", { + page: next + }) === false) return this.nexttab(); - tabEditors.set(next); + tabs.set(next); }, previoustab : function(){ @@ -307,11 +310,14 @@ module.exports = ext.register("ext/tabbehaviors/tabbehaviors", { this.$tabAccessCycle = this.accessed.length; } + var tabs = tabEditors; var next = this.accessed[n]; - if (next == tabEditors.getPage()) + if (next == tabs.getPage() || ide.dispatchEvent("beforeprevioustab", { + page: next + }) === false) return this.previoustab(); - - tabEditors.set(next); + + tabs.set(next); }, gototabright: function() { From 20954ade7c5922c8cc610b42e186176028948d94 Mon Sep 17 00:00:00 2001 From: mikedeboer Date: Wed, 18 Jan 2012 15:16:57 +0100 Subject: [PATCH 42/69] fixed close-tab behavior when invoked with Option-W hotkey --- client/ext/splitview/splitview.js | 7 +++++++ client/ext/tabbehaviors/tabbehaviors.js | 10 ++++++++-- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/client/ext/splitview/splitview.js b/client/ext/splitview/splitview.js index c48bd1c5f1b..cdb989d3fd8 100644 --- a/client/ext/splitview/splitview.js +++ b/client/ext/splitview/splitview.js @@ -98,6 +98,13 @@ module.exports = ext.register("ext/splitview/splitview", { return onAccessTabbing(e); }); + ide.addEventListener("beforeclosetab", function(e) { + var split = Splits.get(e.page)[0]; + if (!Splits.isActive(split)) + return; + e.returnValue = split.pairs[split.activePage].page; + }); + ide.addEventListener("correctactivepage", function(e) { var split = Splits.getActive(); var editor = Editors.currentEditor && Editors.currentEditor.amlEditor; diff --git a/client/ext/tabbehaviors/tabbehaviors.js b/client/ext/tabbehaviors/tabbehaviors.js index 15fa52cadc0..6220ca46cfe 100644 --- a/client/ext/tabbehaviors/tabbehaviors.js +++ b/client/ext/tabbehaviors/tabbehaviors.js @@ -209,8 +209,14 @@ module.exports = ext.register("ext/tabbehaviors/tabbehaviors", { }, closetab: function(page) { - if (!page) + if (!page) { page = tabEditors.getPage(); + var corrected = ide.dispatchEvent("beforeclosetab", { + page: page + }); + if (corrected) + page = corrected; + } if (page) tabEditors.remove(page); @@ -222,7 +228,7 @@ module.exports = ext.register("ext/tabbehaviors/tabbehaviors", { this.closeallbutme(1, callback); }, - // ignore is the page that shouldn't be closed, null to close all tabs + // ignore is the page that shouldn't be closed. closeallbutme: function(ignore, callback) { ignore = ignore || tabEditors.getPage(); this.changedPages = []; From 61f803e03d0ac0044f11488dc885ee81a33eaf3d Mon Sep 17 00:00:00 2001 From: mikedeboer Date: Wed, 18 Jan 2012 15:37:52 +0100 Subject: [PATCH 43/69] Fixed interaction of Search and Replace dialog with split views. Fixes #775 --- client/ext/searchreplace/searchreplace.js | 2 +- client/ext/splitview/splits.js | 8 +++++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/client/ext/searchreplace/searchreplace.js b/client/ext/searchreplace/searchreplace.js index 16d4e2dbbd0..a3a6e173eac 100644 --- a/client/ext/searchreplace/searchreplace.js +++ b/client/ext/searchreplace/searchreplace.js @@ -254,7 +254,7 @@ module.exports = ext.register("ext/searchreplace/searchreplace", { }, replaceAll: function() { - if (!this.editor) + if (!this.$editor) this.setEditor(); if (!this.$editor) return; diff --git a/client/ext/splitview/splits.js b/client/ext/splitview/splits.js index c45de9e7f91..aa571f4a18c 100644 --- a/client/ext/splitview/splits.js +++ b/client/ext/splitview/splits.js @@ -12,6 +12,7 @@ var ide = require("core/ide"); var Grids = require("ext/splitview/grids"); var ZManager = require("ext/splitview/zmanager"); var Editors = require("ext/editors/editors"); +var SearchReplace = require("ext/searchreplace/searchreplace"); var HashHandler = require("ace/keyboard/hash_handler").HashHandler; var Splits = []; var EditorClones = {}; @@ -149,8 +150,11 @@ exports.hide = function(split, notGrid) { if (split === ActiveSplit) ActiveSplit = null; - if (previousEditor) + if (previousEditor) { Editors.currentEditor.amlEditor = previousEditor; + // invalidate search-replace's cache of the editor object + delete SearchReplace.$editor; + } return this; }; @@ -475,6 +479,8 @@ function onEditorFocus(editor) { if (!previousEditor) previousEditor = Editors.currentEditor.amlEditor; Editors.currentEditor.amlEditor = editor; + // invalidate search-replace's cache of the editor object + delete SearchReplace.$editor; } splits.forEach(function(split) { From 0b3785f9bd7f5c16c386965c7b793ce110034716 Mon Sep 17 00:00:00 2001 From: mikedeboer Date: Wed, 18 Jan 2012 15:54:57 +0100 Subject: [PATCH 44/69] Fixed inconsistent grid layout after moving tabs around. Fixes #777 --- client/ext/splitview/splits.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/client/ext/splitview/splits.js b/client/ext/splitview/splits.js index aa571f4a18c..ce7be1665fb 100644 --- a/client/ext/splitview/splits.js +++ b/client/ext/splitview/splits.js @@ -175,8 +175,9 @@ exports.update = function(split, gridLayout) { if (split.pairs.length === 1) { var editor = page.$editor.amlEditor; if (EditorClones[editor.localName]) { - for (var clone, i = 0, l = EditorClones[editor.localName].length; i < l; ++i) { - clone = EditorClones[editor.localName][i]; + var editors = [EditorClones[editor.localName].original].concat(EditorClones[editor.localName]); + for (var clone, i = 0, l = editors.length; i < l; ++i) { + clone = editors[i]; clone.hide(); apf.document.body.appendChild(clone); } From 05d834fe592bf42807587a1fe624e8a0b548708c Mon Sep 17 00:00:00 2001 From: mikedeboer Date: Thu, 19 Jan 2012 11:30:41 +0100 Subject: [PATCH 45/69] added look of horz splitter to vert splitter as well, and made the gradient adjustable. Fixes #772. Fixes #784 --- client/style/images/splitter_dark_bg.png | Bin 136 -> 0 bytes client/style/images/splitter_dark_grab.png | Bin 145 -> 999 bytes client/style/skins.xml | 59 ++++++++++++--------- 3 files changed, 34 insertions(+), 25 deletions(-) delete mode 100644 client/style/images/splitter_dark_bg.png diff --git a/client/style/images/splitter_dark_bg.png b/client/style/images/splitter_dark_bg.png deleted file mode 100644 index eb586417a03a2e7ae8cf9cbdc9f27ccd26066b27..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 136 zcmeAS@N?(olHy`uVBq!ia0vp^azM<+!3HF+Y}E?^Qj#UE5hcO-X(i=}MX3yqDfvmM z3ZA)%>8U}fi7AzZCsS>JinKgk978H@CB1q3vNK77;lQF5D}JOp==}TnS^aZE#Gmi) iUuI@tkzhD{gWqbKX!v8GE(T9mKbLh*2~7Z60WI{39<*Ho!?+tv^c8dzn7-{18@nJiOeUji z^Rm|G>eyUykW2bp>x7#c%8f?B@u+7V`Sv4Z&HLB^-INu3cr4OTy&l&Z9?R5FE>DBu zqsIQ#=>8R*p6ECjTWSmjAGvheeLB{k#ho$wB<|()Y$^Ow`Lw^kU5BvK{<^y!yE5~5 z=VSHo=fV32Ba>ja`upf;`&OxW?fgM)?Nw)U^U3&|v(I8b;MI`}%!inMqk4UEa_eQM mvpl}{eCy&1W$k{AIem%QOQDU-=&#q&)63@;^|x~mpZ)=${yy{o delta 53 zcmaFPK9O;PI2SVq8v_G_>;JFb6BX5&7>p-ZFzZRCGP6i96g|=Z_G{Y12B0*9r>mdK II;Vst0FZzX3;+NC diff --git a/client/style/skins.xml b/client/style/skins.xml index 7c3e10c0823..7c1cb78c5aa 100644 --- a/client/style/skins.xml +++ b/client/style/skins.xml @@ -6993,7 +6993,11 @@ .darksplitter { position : relative; z-index : 90000; - background-color: #8c8c8c; + background-color: #e4e4e4; + + display : -moz-box; + display : -webkit-box; + display : -ms-box; } .darksplitterFocus { @@ -7017,47 +7021,52 @@ .darksplitter span { position: absolute; - width: 11px; - height: 3px; - margin-left: 49%; - margin-top: 2px; - background: url(images/splitter_dark_grab.png) no-repeat 0 0; + background-image: url(images/splitter_dark_grab.png); + background-repeat: no-repeat; + background-position: 0 0; } .darksplitter.vertical { - display : -moz-box; - display : -webkit-box; - display : -ms-box; - - border-top: 0px; - border-right: 1px solid #fff; - border-bottom: 0px; - border-left: 1px solid #eaeaea; + border-right: 1px solid #c7c7c7; vertical-align: middle; - width: 3px; + width: 6px; + + background: -moz-linear-gradient(left, #e4e4e4 0%, #d6d5d5 100%); /* FF3.6+ */ + background: -webkit-gradient(linear, left top, right top, color-stop(0%,#e4e4e4), color-stop(100%,#d6d5d5)); /* Chrome,Safari4+ */ + background: -webkit-linear-gradient(left, #e4e4e4 0%,#d6d5d5 100%); /* Chrome10+,Safari5.1+ */ + background: -o-linear-gradient(left, #e4e4e4 0%,#d6d5d5 100%); /* Opera 11.10+ */ + background: -ms-linear-gradient(left, #e4e4e4 0%,#d6d5d5 100%); /* IE10+ */ + background: linear-gradient(left, #e4e4e4 0%,#d6d5d5 100%); /* W3C */ } .darksplitter.vertical span { - display: none; + width: 3px; + height: 11px; + background-position: 0 -3px; + margin-top: 49%; + margin-left: 2px; } .darksplitter.horizontal { position: relative; margin : -2px 0 0 0; - - border: 0; - /*border-top: 1px solid #3d3d3d; - border-right: 0px; - border-bottom: 1px solid #3d3d3d; - border-left: 0px;*/ - height: 6px; - background: url(images/splitter_dark_bg.png) repeat-x 0 0; + + border-top: 1px solid #c7c7c7; + background: -moz-linear-gradient(top, #d6d5d5 0%, #e4e4e4 100%); /* FF3.6+ */ + background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#d6d5d5), color-stop(100%,#e4e4e4)); /* Chrome,Safari4+ */ + background: -webkit-linear-gradient(top, #d6d5d5 0%,#e4e4e4 100%); /* Chrome10+,Safari5.1+ */ + background: -o-linear-gradient(top, #d6d5d5 0%,#e4e4e4 100%); /* Opera 11.10+ */ + background: -ms-linear-gradient(top, #d6d5d5 0%,#e4e4e4 100%); /* IE10+ */ + background: linear-gradient(top, #d6d5d5 0%,#e4e4e4 100%); /* W3C */ } .darksplitter.horizontal span { - display: inline; + width: 11px; + height: 3px; + margin-left: 49%; + margin-top: 1px; } ]]> From a0fd9fe1b47c17133896bc7341d0587a9b3cf7d5 Mon Sep 17 00:00:00 2001 From: mikedeboer Date: Wed, 1 Feb 2012 11:03:20 +0100 Subject: [PATCH 46/69] fixed various styling issues and updated impl for unsupported editors. Fixes #886. Fixes #885. Fixes #884. Fixes #883. Fixes #882. Fixes #689. --- client/ext/splitview/grids.js | 14 ++++++++++++++ client/ext/splitview/splits.js | 17 ++++++++++++----- client/ext/splitview/splitview.js | 25 ++++++++++++++++++++++--- client/style/skins.xml | 16 ++++++++-------- 4 files changed, 56 insertions(+), 16 deletions(-) diff --git a/client/ext/splitview/grids.js b/client/ext/splitview/grids.js index f328a550054..187f60fb792 100644 --- a/client/ext/splitview/grids.js +++ b/client/ext/splitview/grids.js @@ -263,6 +263,20 @@ function createGridNodes(name) { if (timeout) return; grids.dispatchEvent("resize", lastEvent, this); + // correct the positioning of the grabber of each splitter. + // use a setTimeout(..., 0) to make sure the repaint has happened and + // offset values are correct. + setTimeout(function() { + var splitter, grabber; + for (var i = 0, l = blueprint.splitters.length; i < l; ++i) { + splitter = blueprint.splitters[i]; + grabber = splitter.$ext.getElementsByTagName("span")[0]; + if (splitter.$ext.className.indexOf("vertical") > -1) + grabber.style.marginTop = ((splitter.$ext.offsetHeight / 2) - 20) + "px"; + else + grabber.style.marginLeft = ((splitter.$ext.offsetWidth / 2) - 20) + "px"; + } + }); timeout = setTimeout(function(){ timeout = null; }, 100); }); } diff --git a/client/ext/splitview/splits.js b/client/ext/splitview/splits.js index ce7be1665fb..4e36a9414da 100644 --- a/client/ext/splitview/splits.js +++ b/client/ext/splitview/splits.js @@ -324,6 +324,8 @@ exports.is = function(amlNode) { }; exports.isActive = function(split) { + if (!split) + return false; return split === ActiveSplit; }; @@ -332,12 +334,16 @@ exports.getActive = function() { }; exports.setActivePage = function(split, page) { + split = split || ActiveSplit; var old = split.activePage; - var idx = split.activePage = page - ? exports.indexOf(split, page) - : split.activePage; + var idx = split.activePage = (typeof page == "number" && !isNaN(page) + ? page + : !!page + ? exports.indexOf(split, page) + : split.activePage); if (idx == -1) return; + var pair = split.pairs[idx] ? split.pairs[idx] : split.pairs[0]; pair.editor.focus(); if (idx !== old) @@ -489,9 +495,10 @@ function onEditorFocus(editor) { for (var page, i = 0, l = split.pairs.length; i < l; ++i) { page = split.pairs[i].page; if (page === activePage) { - if (split.activePage !== i) + if (split.activePage !== i) { + split.activePage = i; ide.dispatchEvent("pageswitch", { page: page }); - split.activePage = i; + } apf.setStyleClass(page.$button, ActiveClass, [InactiveClass]); } else diff --git a/client/ext/splitview/splitview.js b/client/ext/splitview/splitview.js index cdb989d3fd8..2a0d294406f 100644 --- a/client/ext/splitview/splitview.js +++ b/client/ext/splitview/splitview.js @@ -77,6 +77,12 @@ module.exports = ext.register("ext/splitview/splitview", { return _self.updateSplitView(e.previousPage, e.nextPage); }); + ide.addEventListener("pageswitch", function(e) { + if (!Splits.getActive()) + return; + _self.save(); + }); + ide.addEventListener("closefile", function(e) { _self.onFileClose(e); }); @@ -192,7 +198,7 @@ module.exports = ext.register("ext/splitview/splitview", { return; // enable split view ONLY for code editors for now... - if (pages[idx].$editor.name.indexOf("Code Editor") == -1) + if (!this.isSupportedEditor(curr.$editor, pages[idx].$editor)) return; // pass in null to mutate the active split view Splits.mutate(null, pages[idx]); @@ -277,7 +283,7 @@ module.exports = ext.register("ext/splitview/splitview", { } else if (shiftKey) { // enable split view ONLY for code editors for now... - if (page.$editor.name.indexOf("Code Editor") == -1) + if (!this.isSupportedEditor(activePage.$editor, page.$editor)) return; // tabs can be merged into and unmerged from a splitview by clicking a // tab while holding shift @@ -333,7 +339,7 @@ module.exports = ext.register("ext/splitview/splitview", { } // enable split view ONLY for code editors for now... - if (next.$editor.name.indexOf("Code Editor") > -1) { + if (this.isSupportedEditor(next.$editor)) { mnuCloneView.enable(); mnuSplitAlign.enable(); } @@ -484,6 +490,7 @@ module.exports = ext.register("ext/splitview/splitview", { return pair.page.id; }).join(",")); splitEl.setAttribute("active", Splits.isActive(splits[i]) ? "true" : "false"); + splitEl.setAttribute("activepage", splits[i].activePage + ""); splitEl.setAttribute("layout", splits[i].gridLayout); node.appendChild(splitEl); } @@ -533,6 +540,8 @@ module.exports = ext.register("ext/splitview/splitview", { if (gridLayout) Splits.update(null, gridLayout); + Splits.setActivePage(null, parseInt(nodes[i].getAttribute("activepage"), 10)); + if (apf.isTrue(nodes[i].getAttribute("active"))) active = Splits.getActive(); } @@ -543,6 +552,16 @@ module.exports = ext.register("ext/splitview/splitview", { Splits.show(active); }, + isSupportedEditor: function() { + var editor; + for (var i = 0, l = arguments.length; i < l; ++i) { + editor = arguments[i]; + if (!editor || !editor.name || editor.name.indexOf("Code Editor") == -1) + return false; + } + return true; + }, + enable : function(){ this.nodes.each(function(item){ item.enable(); diff --git a/client/style/skins.xml b/client/style/skins.xml index e27e81c61ab..ba177f334ee 100644 --- a/client/style/skins.xml +++ b/client/style/skins.xml @@ -7028,17 +7028,17 @@ } .darksplitter.vertical { - border-right: 1px solid #c7c7c7; + border-left: 1px solid #c7c7c7; vertical-align: middle; width: 6px; - background: -moz-linear-gradient(left, #e4e4e4 0%, #d6d5d5 100%); /* FF3.6+ */ - background: -webkit-gradient(linear, left top, right top, color-stop(0%,#e4e4e4), color-stop(100%,#d6d5d5)); /* Chrome,Safari4+ */ - background: -webkit-linear-gradient(left, #e4e4e4 0%,#d6d5d5 100%); /* Chrome10+,Safari5.1+ */ - background: -o-linear-gradient(left, #e4e4e4 0%,#d6d5d5 100%); /* Opera 11.10+ */ - background: -ms-linear-gradient(left, #e4e4e4 0%,#d6d5d5 100%); /* IE10+ */ - background: linear-gradient(left, #e4e4e4 0%,#d6d5d5 100%); /* W3C */ + background: -moz-linear-gradient(left, #d6d5d5 0%, #e4e4e4 100%); /* FF3.6+ */ + background: -webkit-gradient(linear, left top, right top, color-stop(0%,#d6d5d5), color-stop(100%,#e4e4e4)); /* Chrome,Safari4+ */ + background: -webkit-linear-gradient(left, #d6d5d5 0%,#e4e4e4 100%); /* Chrome10+,Safari5.1+ */ + background: -o-linear-gradient(left, #d6d5d5 0%,#e4e4e4 100%); /* Opera 11.10+ */ + background: -ms-linear-gradient(left, #d6d5d5 0%,#e4e4e4 100%); /* IE10+ */ + background: linear-gradient(left, #d6d5d5 0%,#e4e4e4 100%); /* W3C */ } .darksplitter.vertical span { @@ -7046,7 +7046,7 @@ height: 11px; background-position: 0 -3px; margin-top: 49%; - margin-left: 2px; + margin-left: 1px; } .darksplitter.horizontal { From 3d2970c53f4044286ca58d574d6f76411e9bd5ee Mon Sep 17 00:00:00 2001 From: mikedeboer Date: Wed, 1 Feb 2012 15:26:19 +0100 Subject: [PATCH 47/69] fixed clone view, some formatting changes and more. Fixes #888 --- client/ext/editors/editors.js | 15 +++---- client/ext/splitview/splits.js | 25 ++++++++---- client/ext/splitview/splitview.js | 66 +++++++++++++++---------------- client/ext/tree/tree_editor.js | 25 ++++++------ 4 files changed, 71 insertions(+), 60 deletions(-) diff --git a/client/ext/editors/editors.js b/client/ext/editors/editors.js index 65f41600f99..1e3561f4462 100644 --- a/client/ext/editors/editors.js +++ b/client/ext/editors/editors.js @@ -372,17 +372,18 @@ module.exports = ext.register("ext/editors/editors", { settings.save(); }, - initEditorEvents: function(fake, model) { - fake.$at.addEventListener("afterchange", function(e) { + initEditorEvents: function(page, model) { + model = model || page.$model; + page.$at.addEventListener("afterchange", function(e) { if (e.action == "reset") { delete this.undo_ptr; return; } var val; - if (fake.$at.ignoreChange) { + if (page.$at.ignoreChange) { val = undefined; - fake.$at.ignoreChange = false; + page.$at.ignoreChange = false; } else if(this.undolength === 0 && !this.undo_ptr) { val = undefined; @@ -393,11 +394,11 @@ module.exports = ext.register("ext/editors/editors", { : undefined; } - if (fake.changed !== val) { - fake.changed = val; + if (page.changed !== val) { + page.changed = val; model.setQueryValue("@changed", (val ? "1" : "0")); - var node = fake.$doc.getNode(); + var node = page.$doc.getNode(); ide.dispatchEvent("updatefile", { changed : val ? 1 : 0, xmlNode : node diff --git a/client/ext/splitview/splits.js b/client/ext/splitview/splits.js index 4e36a9414da..33d65451497 100644 --- a/client/ext/splitview/splits.js +++ b/client/ext/splitview/splits.js @@ -130,7 +130,8 @@ exports.show = function(split) { for (i = 0, l = split.pairs.length; i < l; ++i) { split.pairs[i].page.$activateButton(); split.pairs[i].editor.show(); - exports.consolidateEditorSession(split.pairs[i].page, split.pairs[i].editor); + if (!(split.clone && split.pairs[i].page === split.clone)) + exports.consolidateEditorSession(split.pairs[i].page, split.pairs[i].editor); } ActiveSplit = split; @@ -225,7 +226,7 @@ exports.update = function(split, gridLayout) { return this; }; -exports.mutate = function(split, page) { +exports.mutate = function(split, page, type) { split = split || split === null ? ActiveSplit : null; var tabs = tabEditors; var activePage = tabs.getPage(); @@ -265,7 +266,11 @@ exports.mutate = function(split, page) { if (page === activePage) return true; + if (type && type == "clone") + activePage.$editor.amlEditor = clones.original; split = this.create(activePage); + if (type && type == "clone") + split.clone = page; } var editorToUse; @@ -290,8 +295,9 @@ exports.mutate = function(split, page) { page: page, editor: editorToUse }); - //console.log("setting model of ", editorToUse.id, "to", page.$model.data.xml); - exports.consolidateEditorSession(page, editorToUse); + + if (!(split.clone && page === split.clone)) + exports.consolidateEditorSession(page, editorToUse); split.zManager.set(editorToUse.$ext); this.show(split); @@ -350,10 +356,6 @@ exports.setActivePage = function(split, page) { ide.dispatchEvent("pageswitch", { page: pair.page }); }; -/* - * Implemented this function, because Array.indexOf() compares objects with '==' - * instead of '==='! - */ exports.indexOf = function(split, obj) { var type = obj.localName.indexOf("page") > -1 ? "page" : "editor"; for (var i = 0, l = split.pairs.length; i < l; ++i) { @@ -363,6 +365,13 @@ exports.indexOf = function(split, obj) { return -1; }; + +exports.getCloneEditor = function(page) { + if (page && page.$editor.amlEditor) + createEditorClones.call(this, page.$editor.amlEditor); + return EditorClones.cloneEditor || null; +}; + function sortEditorsAndPages(split) { var pages = tabEditors.getPages(); split.pairs.sort(function(pair1, pair2) { diff --git a/client/ext/splitview/splitview.js b/client/ext/splitview/splitview.js index 2a0d294406f..cd60b3d42fd 100644 --- a/client/ext/splitview/splitview.js +++ b/client/ext/splitview/splitview.js @@ -368,28 +368,8 @@ module.exports = ext.register("ext/splitview/splitview", { Splits.show(split); mnuSplitAlign.setAttribute("checked", split.gridLayout == "3rows"); - if (split.clone) { - var _self = this; - var page = split.clone; - editor = page.$editor; - + if (split.clone) mnuCloneView.setAttribute("checked", true); - - if (!page.acesession) { - page.acesession = new EditSession(doc.acedoc); - page.acesession.setUndoManager(at); - - doc.addEventListener("prop.value", function(e) { - page.acesession.setValue(e.value || ""); - editor.moveCursorTo(0, 0); - }); - - doc.addEventListener("close", function(){ - _self.endCloneView(page); - }); - } - editor.amlEditor.setProperty("value", page.acesession); - } apf.layout.forceResize(); @@ -418,34 +398,47 @@ module.exports = ext.register("ext/splitview/splitview", { if (split || !doc || !Splits.getEditorSession(page)) return; + var _self = this; var fake = tabEditors.add("{([@changed] == 1 ? '*' : '') + [@name]}", page.$model.data.getAttribute("path") + "_clone", page.$editor.path, page.nextSibling || null); - + fake.contentType = page.contentType; fake.$at = page.$at; fake.$doc = doc; fake.$editor = page.$editor; + fake.$model = page.$model; + + + Splits.mutate(null, fake, "clone"); + + fake.setAttribute("model", fake.$model); fake.setAttribute("tooltip", "[@path]"); fake.setAttribute("class", "{parseInt([@saving], 10) || parseInt([@lookup], 10) ? (tabEditors.getPage(tabEditors.activepage) == this ? 'saving_active' : 'saving') : \ ([@loading] ? (tabEditors.getPage(tabEditors.activepage) == this ? 'loading_active' : 'loading') : '')}" ); - fake.setAttribute("model", fake.$model = page.$model); + + Editors.initEditorEvents(fake, page.$model); + + var editor = Splits.getCloneEditor(page); + + fake.acesession = new EditSession(doc.acedoc); + fake.acesession.setUndoManager(fake.$at); + + doc.addEventListener("prop.value", function(e) { + fake.acesession.setValue(e.value || ""); + fake.acesession.moveCursorTo(0, 0); + }); + + editor.setProperty("value", fake.acesession); page.addEventListener("DOMNodeRemovedFromDocument", function(e) { if (typeof tabEditors == "undefined" || !fake || !fake.parentNode) return; - tabEditors.remove(fake); + tabEditors.remove(fake, null, true); }); - Editors.initEditorEvents(fake, page.$model); - - Splits.mutate(null, fake); - - split = Splits.get(fake)[0]; - split.clone = fake; - - Splits.update(split); + Splits.update(); this.save(); @@ -453,12 +446,19 @@ module.exports = ext.register("ext/splitview/splitview", { }, endCloneView: function(page) { + mnuCloneView.setAttribute("checked", false); var split = this.getCloneView(page); if (!split) return; - tabEditors.remove(split.clone); + var fake = split.clone; delete split.clone; + // use setTimeout(..., 0), or else we go into an infinite loop. There are + // three or more use cases to end a cloneView, this function is just one + // of 'em. + setTimeout(function() { + tabEditors.remove(fake, null, true); + }); }, getCloneView: function(page) { diff --git a/client/ext/tree/tree_editor.js b/client/ext/tree/tree_editor.js index 3e8e729d53b..19bc47f54b5 100644 --- a/client/ext/tree/tree_editor.js +++ b/client/ext/tree/tree_editor.js @@ -213,21 +213,21 @@ module.exports = ext.register("ext/editors/editors", { if (init) tabEditors.setAttribute("buttons", "close"); - var model = new apf.model(), - fake = tabEditors.add("{([@changed] == 1 ? '*' : '') + [@name]}", filepath, editor.path, null, function(page){ - page.contentType = contentType; - page.$at = new apf.actiontracker(); - page.$doc = doc; - page.$editor = editor; - - page.setAttribute("model", page.$model = model); - page.$model.load(xmlNode); - }); + var model = new apf.model(); + var fake = tabEditors.add("{([@changed] == 1 ? '*' : '') + [@name]}", filepath, editor.path, null, function(page){ + page.contentType = contentType; + page.$at = new apf.actiontracker(); + page.$doc = doc; + page.$editor = editor; + + page.setAttribute("model", page.$model = model); + page.$model.load(xmlNode); + }); if (init) tabEditors.setAttribute("buttons", "close,scale"); - fake.$at.addEventListener("afterchange", function() { + fake.$at.addEventListener("afterchange", function(e) { if (e.action == "reset") { delete this.undo_ptr; return; @@ -237,7 +237,8 @@ module.exports = ext.register("ext/editors/editors", { if (fake.$at.ignoreChange) { val = undefined; fake.$at.ignoreChange = false; - } else if(this.undolength === 0 && !this.undo_ptr) + } + else if(this.undolength === 0 && !this.undo_ptr) val = undefined; else val = (this.$undostack[this.$undostack.length-1] !== this.undo_ptr) ? 1 : undefined; From 0c29ef35f6ba6c00701712082373b6fef454d2f2 Mon Sep 17 00:00:00 2001 From: mikedeboer Date: Wed, 1 Feb 2012 16:40:06 +0100 Subject: [PATCH 48/69] fixed syntax switching. Fixes #887. --- client/ext/code/code.js | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/client/ext/code/code.js b/client/ext/code/code.js index 8f9dc7dd042..dd176883111 100644 --- a/client/ext/code/code.js +++ b/client/ext/code/code.js @@ -350,12 +350,13 @@ module.exports = ext.register("ext/code/code", { e.node.setAttribute("scriptid", nodes[0].getAttribute("scriptid")); } } - e.doc.editor.ceEditor.afterOpenFile(e.doc.editor.ceEditor.getSession()); + e.doc.editor.amlEditor.afterOpenFile(e.doc.editor.amlEditor.getSession()); } }); tabEditors.addEventListener("afterswitch", function(e) { - ceEditor.afterOpenFile(ceEditor.getSession()); + var editor = _self.amlEditor; + editor.afterOpenFile(editor.getSession()); }); // preload common language modes @@ -420,11 +421,14 @@ module.exports = ext.register("ext/code/code", { delete contentTypes["*" + fileName]; var mime = value.split(";")[0]; - var fileExt = (fileName.lastIndexOf(".") != -1) ? - fileName.split(".").pop() : null; - - if (fileExt && contentTypes[fileExt] !== mime) - delete contentTypes[fileExt]; + var fileExt = (fileName.lastIndexOf(".") != -1) + ? fileName.split(".").pop() + : null; + + // FIXME: WHY would you want to overwrite the default content + // types?!?!?!? + //if (fileExt && value != "auto" && contentTypes[fileExt] !== mime) + // delete contentTypes[fileExt]; var customType = fileExt ? contentTypes[fileExt] : contentTypes["*" + fileName]; From 1c59f0d4f58596e4bca7753a2c906e2e2c0b7681 Mon Sep 17 00:00:00 2001 From: mikedeboer Date: Wed, 1 Feb 2012 17:21:58 +0100 Subject: [PATCH 49/69] minor issue fix --- client/ext/splitview/splitview.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/ext/splitview/splitview.js b/client/ext/splitview/splitview.js index cd60b3d42fd..52c95cac56f 100644 --- a/client/ext/splitview/splitview.js +++ b/client/ext/splitview/splitview.js @@ -427,7 +427,7 @@ module.exports = ext.register("ext/splitview/splitview", { doc.addEventListener("prop.value", function(e) { fake.acesession.setValue(e.value || ""); - fake.acesession.moveCursorTo(0, 0); + editor.$editor.moveCursorTo(0, 0); }); editor.setProperty("value", fake.acesession); From cdf68943bb62a1ee541db0811719b17bbb7d1315 Mon Sep 17 00:00:00 2001 From: mikedeboer Date: Thu, 2 Feb 2012 11:24:29 +0100 Subject: [PATCH 50/69] updated ace. Fixes #771 --- support/ace | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/support/ace b/support/ace index 4b3dd3073e4..0caee61b22a 160000 --- a/support/ace +++ b/support/ace @@ -1 +1 @@ -Subproject commit 4b3dd3073e478108a95ad949029c7aea93d8e6c0 +Subproject commit 0caee61b22ad0391edaa36185f6e39cc12d07fc6 From b85848535b706fe1435a0bea30b12d8c7997c78b Mon Sep 17 00:00:00 2001 From: mikedeboer Date: Thu, 2 Feb 2012 11:29:09 +0100 Subject: [PATCH 51/69] udpated ace. Fixes #779. Fixes #782 --- support/ace | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/support/ace b/support/ace index 0caee61b22a..2ca62bca00b 160000 --- a/support/ace +++ b/support/ace @@ -1 +1 @@ -Subproject commit 0caee61b22ad0391edaa36185f6e39cc12d07fc6 +Subproject commit 2ca62bca00b2560a02d9a2029719af6e14358c13 From aa5a94a056a4e0c30aa0ca55d6fa13f11248adfc Mon Sep 17 00:00:00 2001 From: mikedeboer Date: Thu, 2 Feb 2012 12:22:14 +0100 Subject: [PATCH 52/69] updated ace. Fixes #783 --- support/ace | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/support/ace b/support/ace index 2ca62bca00b..67ba8f5f29e 160000 --- a/support/ace +++ b/support/ace @@ -1 +1 @@ -Subproject commit 2ca62bca00b2560a02d9a2029719af6e14358c13 +Subproject commit 67ba8f5f29ed1d0c3b3d31229d5597a99860f21c From 5947551516f3d078d84acaf20735142d8593f2b8 Mon Sep 17 00:00:00 2001 From: mikedeboer Date: Thu, 2 Feb 2012 12:24:01 +0100 Subject: [PATCH 53/69] fixed horz scroll positioning --- client/ext/splitview/splits.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/client/ext/splitview/splits.js b/client/ext/splitview/splits.js index 33d65451497..e1f0dd247ae 100644 --- a/client/ext/splitview/splits.js +++ b/client/ext/splitview/splits.js @@ -130,6 +130,8 @@ exports.show = function(split) { for (i = 0, l = split.pairs.length; i < l; ++i) { split.pairs[i].page.$activateButton(); split.pairs[i].editor.show(); + if (split.pairs[i].editor.$editor.onScrollLeftChange) + split.pairs[i].editor.$editor.onScrollLeftChange(); if (!(split.clone && split.pairs[i].page === split.clone)) exports.consolidateEditorSession(split.pairs[i].page, split.pairs[i].editor); } From 9d109500f2043ef7c20e7aa2645c99d91977f974 Mon Sep 17 00:00:00 2001 From: mikedeboer Date: Thu, 2 Feb 2012 15:35:12 +0100 Subject: [PATCH 54/69] fix clone view from within a splitview --- client/ext/splitview/splits.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/client/ext/splitview/splits.js b/client/ext/splitview/splits.js index e1f0dd247ae..40b314125dc 100644 --- a/client/ext/splitview/splits.js +++ b/client/ext/splitview/splits.js @@ -230,6 +230,7 @@ exports.update = function(split, gridLayout) { exports.mutate = function(split, page, type) { split = split || split === null ? ActiveSplit : null; + type = type || "default"; var tabs = tabEditors; var activePage = tabs.getPage(); var pairIdx = split ? exports.indexOf(split, page) : -1; @@ -271,9 +272,9 @@ exports.mutate = function(split, page, type) { if (type && type == "clone") activePage.$editor.amlEditor = clones.original; split = this.create(activePage); - if (type && type == "clone") - split.clone = page; } + if (!split.clone && type == "clone") + split.clone = page; var editorToUse; if (split.clone && page === split.clone) { From e78967a8bafa6c7e989f6329edcfb66c1fbbc4ee Mon Sep 17 00:00:00 2001 From: mikedeboer Date: Fri, 3 Feb 2012 13:31:23 +0100 Subject: [PATCH 55/69] set keybindings for clone editor too. Fixes #901 --- client/ext/splitview/splits.js | 4 ++++ client/ext/splitview/splitview.js | 7 ++++++- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/client/ext/splitview/splits.js b/client/ext/splitview/splits.js index 40b314125dc..8efe937abec 100644 --- a/client/ext/splitview/splits.js +++ b/client/ext/splitview/splits.js @@ -398,6 +398,10 @@ function createEditorClones(editor) { addEditorListeners.call(this, EditorClones.cloneEditor); + EditorClones.cloneEditor.$editor.commands = previousEditor.$editor.commands; + if (previousEditor.$editor.getKeyboardHandler()) + EditorClones.cloneEditor.$editor.setKeyboardHandler(previousEditor.$editor.getKeyboardHandler()); + // add listeners to ceEditor properties that also need to be applied to // other editor instances: function setProp(which, value) { diff --git a/client/ext/splitview/splitview.js b/client/ext/splitview/splitview.js index 52c95cac56f..f9f3e2a911b 100644 --- a/client/ext/splitview/splitview.js +++ b/client/ext/splitview/splitview.js @@ -405,10 +405,10 @@ module.exports = ext.register("ext/splitview/splitview", { fake.contentType = page.contentType; fake.$at = page.$at; fake.$doc = doc; + //doc.$page = fake; fake.$editor = page.$editor; fake.$model = page.$model; - Splits.mutate(null, fake, "clone"); fake.setAttribute("model", fake.$model); @@ -418,6 +418,11 @@ module.exports = ext.register("ext/splitview/splitview", { ([@loading] ? (tabEditors.getPage(tabEditors.activepage) == this ? 'loading_active' : 'loading') : '')}" ); + page.addEventListener("prop.caption", function(e) { + fake.setProperty("caption", e.value); + }); + fake.setProperty("caption", page.caption); + Editors.initEditorEvents(fake, page.$model); var editor = Splits.getCloneEditor(page); From 37d976264ab0fb29e761244c66cb54d2b34ff101 Mon Sep 17 00:00:00 2001 From: mikedeboer Date: Fri, 3 Feb 2012 15:13:07 +0100 Subject: [PATCH 56/69] minimum right margin for searchbox. Fixes #904 --- client/ext/splitview/splits.js | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/client/ext/splitview/splits.js b/client/ext/splitview/splits.js index 8efe937abec..2ff1bdc9e69 100644 --- a/client/ext/splitview/splits.js +++ b/client/ext/splitview/splits.js @@ -564,20 +564,23 @@ function correctQuickSearchDialog(e) { width: parent.$ext.offsetWidth, height: parent.$ext.offsetHeight }; + var minRight = 30; if (!searchWindow && self["winQuickSearch"]) { searchWindow = self["winQuickSearch"]; searchPos = apf.getStyle(searchWindow.$ext, "right"); if (searchPos == "auto") - searchPos = "30px"; + searchPos = minRight + "px"; } if (searchWindow) { + // hardcoded searchbox width (350px) and added 30px margin on the right + var maxRight = parentDims.width - 380; var right = parentDims.width - editorPos[0] - editorDims.width + 30; var top = editorPos[1]; //console.log("editorPos", editorPos,"editorDims",JSON.stringify(editorDims),"parentDims",JSON.stringify(parentDims),"right",right,"top",top); var to = Math.max(top, 0); return { - right: Math.max(right, 30), + right: Math.max(Math.min(right, maxRight), minRight), zIndex: parseInt(editor.$ext.style.zIndex, 10) + 1, from: !e || e.anim == "out" ? to - 27 : 0, to: !e || e.anim == "out" ? to : (to - 30), From 5130336e1eed05bf1519b6f30b8218f1ea7dfdef Mon Sep 17 00:00:00 2001 From: mikedeboer Date: Mon, 6 Feb 2012 15:59:15 +0100 Subject: [PATCH 57/69] fixed syntax menu selection setting. Fixes #887 --- client/ext/code/code.js | 8 ++++++++ client/ext/splitview/splitview.js | 17 +++++++++++++++++ 2 files changed, 25 insertions(+) diff --git a/client/ext/code/code.js b/client/ext/code/code.js index dd176883111..f1f30f1e3a4 100644 --- a/client/ext/code/code.js +++ b/client/ext/code/code.js @@ -226,6 +226,14 @@ module.exports = ext.register("ext/code/code", { return "text"; }, + + getContentType : function(node) { + var syntax = this.getSyntax(node); + if (!syntax) + return "auto"; + + return contentTypes[syntax] || (syntax == "text" ? "text/plain" : "auto"); + }, getSelection : function(){ if (typeof this.amlEditor == "undefined") diff --git a/client/ext/splitview/splitview.js b/client/ext/splitview/splitview.js index f9f3e2a911b..1dd51b52b65 100644 --- a/client/ext/splitview/splitview.js +++ b/client/ext/splitview/splitview.js @@ -14,6 +14,7 @@ var css = require("text!ext/splitview/splitview.css"); var Editors = require("ext/editors/editors"); var Tabbehaviors = require("ext/tabbehaviors/tabbehaviors"); var Settings = require("ext/settings/settings"); +var Code = require("ext/code/code"); var Splits = require("ext/splitview/splits"); @@ -81,6 +82,22 @@ module.exports = ext.register("ext/splitview/splitview", { if (!Splits.getActive()) return; _self.save(); + + if (typeof mnuSyntax == "undefined") + return; + + var item; + var syntax = mnuSyntax; + var value = Code.getContentType(e.page.$model.data); + for (var i = 0, l = syntax.childNodes.length; i < l; ++i) { + item = syntax.childNodes[i]; + if (!item || !item.localName || item.localName != "item") + continue; + if (item.value == value) { + item.select(); + break; + } + } }); ide.addEventListener("closefile", function(e) { From 1417bb208df23d74c14bb007d2750cf2c8c769bd Mon Sep 17 00:00:00 2001 From: mikedeboer Date: Tue, 7 Feb 2012 10:51:44 +0100 Subject: [PATCH 58/69] refactor of the clone view --- client/ext/code/code.js | 2 + client/ext/editors/editors.js | 27 ++++-- client/ext/imgview/imgview.js | 4 + client/ext/splitview/splitview.js | 135 +++++++++++++++--------------- client/style/skins.xml | 24 +++--- 5 files changed, 102 insertions(+), 90 deletions(-) diff --git a/client/ext/code/code.js b/client/ext/code/code.js index f1f30f1e3a4..7d9d0d844ce 100644 --- a/client/ext/code/code.js +++ b/client/ext/code/code.js @@ -287,6 +287,8 @@ module.exports = ext.register("ext/code/code", { //??? destroy doc.acesession }); + + doc.dispatchEvent("init"); } this.amlEditor.setProperty("value", doc.acesession); diff --git a/client/ext/editors/editors.js b/client/ext/editors/editors.js index 1e3561f4462..804b85cb9d0 100644 --- a/client/ext/editors/editors.js +++ b/client/ext/editors/editors.js @@ -283,15 +283,17 @@ module.exports = ext.register("ext/editors/editors", { this.afterswitch({nextPage: page, previousPage: {type: lastType}}); }, - openEditor : function(doc, init, active) { + openEditor : function(doc, init, active, forceOpen) { var xmlNode = doc.getNode(); var filepath = xmlNode.getAttribute("path"); var tabs = tabEditors; - var page = tabs.getPage(filepath); - if (page) { - tabs.set(page); - return; + if (!forceOpen) { + var page = tabs.getPage(filepath); + if (page) { + tabs.set(page); + return; + } } var fileExtension = (xmlNode.getAttribute("path") || "").split(".").pop(); @@ -341,6 +343,12 @@ module.exports = ext.register("ext/editors/editors", { }); this.initEditorEvents(fake, model); + + ide.dispatchEvent("tab.create", { + page: fake, + model: model, + doc: doc + }); if (init && !active) return; @@ -542,7 +550,7 @@ module.exports = ext.register("ext/editors/editors", { }); ide.addEventListener("openfile", function(e){ - _self.openEditor(e.doc, e.init, e.active); + _self.openEditor(e.doc, e.init, e.active, e.forceOpen); }); ide.addEventListener("filenotfound", function(e) { @@ -621,9 +629,10 @@ module.exports = ext.register("ext/editors/editors", { } ide.dispatchEvent("openfile", { - doc : doc, - init : true, - active : active + doc : doc, + init : true, + forceOpen: true, + active : active ? active == node.getAttribute("path") : i == l - 1 }); diff --git a/client/ext/imgview/imgview.js b/client/ext/imgview/imgview.js index 664c8e3730e..05401d62db4 100644 --- a/client/ext/imgview/imgview.js +++ b/client/ext/imgview/imgview.js @@ -43,6 +43,10 @@ module.exports = ext.register("ext/imgview/imgview", { setDocument : function(doc, actiontracker){ doc.session = doc.getNode().getAttribute("path"); imgEditor.setProperty("value", doc.session); + if (!doc.isInited) { + doc.isInited = true; + doc.dispatchEvent("init"); + } }, hook : function() {}, diff --git a/client/ext/splitview/splitview.js b/client/ext/splitview/splitview.js index 1dd51b52b65..71f313fcd79 100644 --- a/client/ext/splitview/splitview.js +++ b/client/ext/splitview/splitview.js @@ -170,7 +170,7 @@ module.exports = ext.register("ext/splitview/splitview", { }); ide.addEventListener("activepagemodel", function(e) { - var page = tabEditors.getPage(); + var page = tabs.getPage(); var split = Splits.get(page)[0]; if (!split || !Splits.isActive(split)) return; @@ -178,6 +178,62 @@ module.exports = ext.register("ext/splitview/splitview", { e.returnValue = split.pairs[split.activePage || 0].page.$model; }); + ide.addEventListener("tab.create", function(e) { + var page = e.page; + var xmlNode = e.doc.getNode(); + if (!apf.isTrue(xmlNode.getAttribute("clone"))) + return; + + var id = page.id; + var pages = tabs.getPages(); + var origPage; + // loop to find 2nd tab. + for (var i = 0, l = pages.length; i < l; ++i) { + if (pages[i] !== page && pages[i].id == id) { + origPage = pages[i]; + break; + } + } + // if 2nd page found, join em! + if (!origPage) + return; + + page.$doc = origPage.$doc; + page.setAttribute("actiontracker", origPage.$at); + page.$at = origPage.$at; + + if (!page.$doc.acedoc) + page.$doc.addEventListener("init", cont); + else + cont(); + + function cont() { + var editor = Splits.getCloneEditor(page); + + page.acesession = new EditSession(page.$doc.acedoc); + page.acesession.setUndoManager(page.$at); + + page.$doc.addEventListener("prop.value", function(e) { + page.acesession.setValue(e.value || ""); + editor.$editor.moveCursorTo(0, 0); + }); + + editor.setProperty("value", page.acesession); + + Splits.mutate(null, page, "clone"); + + page.addEventListener("DOMNodeRemovedFromDocument", function(e) { + if (!tabs.parentNode || !page.parentNode) + return; + tabs.remove(page, null, true); + }); + + Splits.update(); + + _self.save(); + } + }); + Splits.init(this); }, @@ -414,57 +470,9 @@ module.exports = ext.register("ext/splitview/splitview", { if (split || !doc || !Splits.getEditorSession(page)) return; - - var _self = this; - var fake = tabEditors.add("{([@changed] == 1 ? '*' : '') + [@name]}", page.$model.data.getAttribute("path") - + "_clone", page.$editor.path, page.nextSibling || null); - fake.contentType = page.contentType; - fake.$at = page.$at; - fake.$doc = doc; - //doc.$page = fake; - fake.$editor = page.$editor; - fake.$model = page.$model; - - Splits.mutate(null, fake, "clone"); - - fake.setAttribute("model", fake.$model); - fake.setAttribute("tooltip", "[@path]"); - fake.setAttribute("class", - "{parseInt([@saving], 10) || parseInt([@lookup], 10) ? (tabEditors.getPage(tabEditors.activepage) == this ? 'saving_active' : 'saving') : \ - ([@loading] ? (tabEditors.getPage(tabEditors.activepage) == this ? 'loading_active' : 'loading') : '')}" - ); - - page.addEventListener("prop.caption", function(e) { - fake.setProperty("caption", e.value); - }); - fake.setProperty("caption", page.caption); - - Editors.initEditorEvents(fake, page.$model); - - var editor = Splits.getCloneEditor(page); - - fake.acesession = new EditSession(doc.acedoc); - fake.acesession.setUndoManager(fake.$at); - - doc.addEventListener("prop.value", function(e) { - fake.acesession.setValue(e.value || ""); - editor.$editor.moveCursorTo(0, 0); - }); - - editor.setProperty("value", fake.acesession); - - page.addEventListener("DOMNodeRemovedFromDocument", function(e) { - if (typeof tabEditors == "undefined" || !fake || !fake.parentNode) - return; - tabEditors.remove(fake, null, true); - }); - - Splits.update(); - - this.save(); - - return fake; + apf.xmldb.setAttribute(doc.getNode(), "clone", true); + Editors.openEditor(doc, false, false, true); }, endCloneView: function(page) { @@ -520,6 +528,7 @@ module.exports = ext.register("ext/splitview/splitview", { }, restore: function(settings) { + return; // no tabs open... don't bother ;) var tabs = tabEditors; if (tabs.getPages().length <= 1) @@ -538,25 +547,13 @@ module.exports = ext.register("ext/splitview/splitview", { pageSet = false; gridLayout = nodes[i].getAttribute("layout") || null; for (j = 0, l2 = ids.length; j < l2; ++j) { - if (ids[j].indexOf("_clone") > -1) { - page = tabs.getPage(ids[j].replace("_clone", "")); - if (page) { - if (!pageSet) { - tabs.set(page); - pageSet = true; - } - this.startCloneView(page); - } - } - else { - page = tabs.getPage(ids[j]); - if (page) { - if (!pageSet) { - tabs.set(page); - pageSet = true; - } - Splits.mutate(null, page); + page = tabs.getPage(ids[j]); + if (page) { + if (!pageSet) { + tabs.set(page); + pageSet = true; } + Splits.mutate(null, page); } } if (gridLayout) diff --git a/client/style/skins.xml b/client/style/skins.xml index 284f5803f19..d6ed37ed575 100644 --- a/client/style/skins.xml +++ b/client/style/skins.xml @@ -7038,12 +7038,12 @@ vertical-align: middle; width: 6px; - background: -moz-linear-gradient(left, #d6d5d5 0%, #e4e4e4 100%); /* FF3.6+ */ - background: -webkit-gradient(linear, left top, right top, color-stop(0%,#d6d5d5), color-stop(100%,#e4e4e4)); /* Chrome,Safari4+ */ - background: -webkit-linear-gradient(left, #d6d5d5 0%,#e4e4e4 100%); /* Chrome10+,Safari5.1+ */ - background: -o-linear-gradient(left, #d6d5d5 0%,#e4e4e4 100%); /* Opera 11.10+ */ - background: -ms-linear-gradient(left, #d6d5d5 0%,#e4e4e4 100%); /* IE10+ */ - background: linear-gradient(left, #d6d5d5 0%,#e4e4e4 100%); /* W3C */ + background: -moz-linear-gradient(left, #dbdbdb 0%, #e8e8e8 100%); /* FF3.6+ */ + background: -webkit-gradient(linear, left top, right top, color-stop(0%,#d6d5d5), color-stop(100%,#e8e8e8)); /* Chrome,Safari4+ */ + background: -webkit-linear-gradient(left, #dbdbdb 0%,#e8e8e8 100%); /* Chrome10+,Safari5.1+ */ + background: -o-linear-gradient(left, #dbdbdb 0%,#e8e8e8 100%); /* Opera 11.10+ */ + background: -ms-linear-gradient(left, #dbdbdb 0%,#e8e8e8 100%); /* IE10+ */ + background: linear-gradient(left, #dbdbdb 0%,#e8e8e8 100%); /* W3C */ } .darksplitter.vertical span { @@ -7060,12 +7060,12 @@ height: 6px; border-top: 1px solid #c7c7c7; - background: -moz-linear-gradient(top, #d6d5d5 0%, #e4e4e4 100%); /* FF3.6+ */ - background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#d6d5d5), color-stop(100%,#e4e4e4)); /* Chrome,Safari4+ */ - background: -webkit-linear-gradient(top, #d6d5d5 0%,#e4e4e4 100%); /* Chrome10+,Safari5.1+ */ - background: -o-linear-gradient(top, #d6d5d5 0%,#e4e4e4 100%); /* Opera 11.10+ */ - background: -ms-linear-gradient(top, #d6d5d5 0%,#e4e4e4 100%); /* IE10+ */ - background: linear-gradient(top, #d6d5d5 0%,#e4e4e4 100%); /* W3C */ + background: -moz-linear-gradient(top, #d6d5d5 0%, #e8e8e8 100%); /* FF3.6+ */ + background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#d6d5d5), color-stop(100%,#e8e8e8)); /* Chrome,Safari4+ */ + background: -webkit-linear-gradient(top, #d6d5d5 0%,#e8e8e8 100%); /* Chrome10+,Safari5.1+ */ + background: -o-linear-gradient(top, #d6d5d5 0%,#e8e8e8 100%); /* Opera 11.10+ */ + background: -ms-linear-gradient(top, #d6d5d5 0%,#e8e8e8 100%); /* IE10+ */ + background: linear-gradient(top, #d6d5d5 0%,#e8e8e8 100%); /* W3C */ } .darksplitter.horizontal span { From 456688835291c16e0784542bdbb3179bdf72eb1c Mon Sep 17 00:00:00 2001 From: mikedeboer Date: Tue, 7 Feb 2012 23:54:37 +0100 Subject: [PATCH 59/69] clone view fixes and updates --- client/ext/splitview/grids.js | 6 +- client/ext/splitview/splits.js | 81 ++++++++++---- client/ext/splitview/splitview.js | 171 ++++++++++++++++++++++-------- 3 files changed, 191 insertions(+), 67 deletions(-) diff --git a/client/ext/splitview/grids.js b/client/ext/splitview/grids.js index 187f60fb792..b8f855144c6 100644 --- a/client/ext/splitview/grids.js +++ b/client/ext/splitview/grids.js @@ -284,12 +284,14 @@ function createGridNodes(name) { function insertEditorAt(parent, editor, insertPoint) { //console.log("insertEditorAt",parent, editor, insertPoint); + var nextPoint; var inserted = false; //var count = 0; while (!inserted) { //console.log("round", ++count, "tags:",parent.tagName, insertPoint[0]); - if (parent.tagName.indexOf(insertPoint.shift()) == -1) { - console.log(parent.tagName, insertPoint); + nextPoint = insertPoint.shift(); + if (parent.tagName.indexOf(nextPoint) == -1) { + console.log(parent.tagName, nextPoint, insertPoint); throw new Error("No valid insertion point found for editor"); } diff --git a/client/ext/splitview/splits.js b/client/ext/splitview/splits.js index 2ff1bdc9e69..2510ab152ba 100644 --- a/client/ext/splitview/splits.js +++ b/client/ext/splitview/splits.js @@ -21,6 +21,12 @@ var InactiveClass = "splitview_inactive"; var NPlusOneClass = "splitview_nplus1"; var SplitView, ActiveSplit; +var K = apf.K; +var CloneUndoManager = exports.CloneUndoManager = {}; +["execute", "undo", "redo", "reset", "hasUndo", "hasRedo"].forEach(function(func) { + CloneUndoManager[func] = K; +}); + exports.init = function(splitView) { SplitView = splitView; @@ -91,8 +97,6 @@ exports.create = function(page, gridLayout) { gridLayout = Grids.init(gridLayout); var editor = page.$editor.amlEditor; - editor.setAttribute("model", page.$model); - editor.setAttribute("actiontracker", page.$at); exports.consolidateEditorSession(page, editor); var split = { @@ -109,6 +113,20 @@ exports.create = function(page, gridLayout) { return split; }; +exports.set = function(splits) { + if (!apf.isArray(splits)) + return; + + Splits = [].concat(splits); + for (var split, i = 0, l = Splits.length; i < l; ++i) { + split = Splits[i]; + split.gridLayout = Grids.init(split.gridLayout); + split.activePage = split.activePage || 0; + if (!split.zManager) + split.zManager = new ZManager(); + } +}; + exports.show = function(split) { if (!split || split === ActiveSplit) return this; @@ -221,8 +239,11 @@ exports.update = function(split, gridLayout) { // make sure the buttons of the pages in the active split are highlighted if (split === ActiveSplit) { - for (var i = 0, l = split.pairs.length; i < l; ++i) + for (var i = 0, l = split.pairs.length; i < l; ++i) { split.pairs[i].page.$activateButton(); + if (!(split.clone && split.pairs[i].page === split.clone)) + exports.consolidateEditorSession(split.pairs[i].page, split.pairs[i].editor); + } } return this; @@ -276,21 +297,7 @@ exports.mutate = function(split, page, type) { if (!split.clone && type == "clone") split.clone = page; - var editorToUse; - if (split.clone && page === split.clone) { - editorToUse = EditorClones.cloneEditor; - } - else { - for (var i = 0, l = clones.length; i < l; ++i) { - if (exports.indexOf(split, clones[i]) == -1) { - editorToUse = clones[i]; - break; - } - } - } - if (!editorToUse && exports.indexOf(split, clones.original) == -1) - editorToUse = clones.original; - + var editorToUse = this.getEditor(split, page); if (!editorToUse) throw new Error("Splitview fatal error: no editor available to use."); @@ -309,6 +316,26 @@ exports.mutate = function(split, page, type) { return true; }; +exports.getEditor = function(split, page) { + var editorToUse; + var clones = createEditorClones.call(this, page.$editor.amlEditor); + if (split.clone && page === split.clone) { + editorToUse = EditorClones.cloneEditor; + } + else { + for (var i = 0, l = clones.length; i < l; ++i) { + if (exports.indexOf(split, clones[i]) == -1) { + editorToUse = clones[i]; + break; + } + } + } + if (!editorToUse && exports.indexOf(split, clones.original) == -1) + editorToUse = clones.original; + + return editorToUse; +}; + exports.get = function(amlNode) { if (!amlNode) return [].concat(Splits); @@ -344,6 +371,9 @@ exports.getActive = function() { exports.setActivePage = function(split, page) { split = split || ActiveSplit; + if (!split) + return; + var old = split.activePage; var idx = split.activePage = (typeof page == "number" && !isNaN(page) ? page @@ -454,7 +484,7 @@ exports.getEditorSession = function(page) { var doc = page.$doc; if (!doc) return null; - return doc.acesession || doc.session || null; + return page.acesession || doc.acesession || doc.session || null; }; exports.consolidateEditorSession = function(page, editor) { @@ -508,17 +538,26 @@ function onEditorFocus(editor) { splits.forEach(function(split) { var activePage = split.pairs[exports.indexOf(split, editor)].page; - for (var page, i = 0, l = split.pairs.length; i < l; ++i) { + var isClone = !!split.clone && split.clone.id; + for (var page, session, i = 0, l = split.pairs.length; i < l; ++i) { page = split.pairs[i].page; + session = exports.getEditorSession(page); if (page === activePage) { + // for clone views, the UndoManagers need to be swapped. + if (session && isClone && page.id == isClone) + session.setUndoManager(page.$at); if (split.activePage !== i) { split.activePage = i; ide.dispatchEvent("pageswitch", { page: page }); } apf.setStyleClass(page.$button, ActiveClass, [InactiveClass]); } - else + else { + // for clone views, the UndoManagers need to be swapped. + if (session && isClone && page.id == isClone) + session.setUndoManager(CloneUndoManager); apf.setStyleClass(page.$button, InactiveClass, [ActiveClass]); + } } }); } diff --git a/client/ext/splitview/splitview.js b/client/ext/splitview/splitview.js index 71f313fcd79..aa0b950f6d4 100644 --- a/client/ext/splitview/splitview.js +++ b/client/ext/splitview/splitview.js @@ -22,6 +22,9 @@ var EditSession = require("ace/edit_session").EditSession; var mnuCloneView, mnuSplitAlign; +var restoring = false; +var restoreQueue = []; + module.exports = ext.register("ext/splitview/splitview", { name : "Split View", dev : "Ajax.org", @@ -194,6 +197,7 @@ module.exports = ext.register("ext/splitview/splitview", { break; } } + // if 2nd page found, join em! if (!origPage) return; @@ -208,10 +212,12 @@ module.exports = ext.register("ext/splitview/splitview", { cont(); function cont() { + tabs.set(origPage); + var editor = Splits.getCloneEditor(page); page.acesession = new EditSession(page.$doc.acedoc); - page.acesession.setUndoManager(page.$at); + page.acesession.setUndoManager(Splits.CloneUndoManager); page.$doc.addEventListener("prop.value", function(e) { page.acesession.setValue(e.value || ""); @@ -220,17 +226,25 @@ module.exports = ext.register("ext/splitview/splitview", { editor.setProperty("value", page.acesession); - Splits.mutate(null, page, "clone"); - - page.addEventListener("DOMNodeRemovedFromDocument", function(e) { - if (!tabs.parentNode || !page.parentNode) - return; - tabs.remove(page, null, true); + var split = Splits.create(origPage); + split.clone = page; + split.pairs.push({ + page: page, + editor: Splits.getEditor(split, page) }); - - Splits.update(); - - _self.save(); + Splits.update(split); + + page.addEventListener("DOMNodeRemovedFromDocument", function() { + _self.endCloneView(page); + }); + + origPage.addEventListener("DOMNodeRemovedFromDocument", function() { + _self.endCloneView(page); + }); + + if (restoreQueue.length) + _self.restore(restoreQueue); + //_self.save(); } }); @@ -360,8 +374,15 @@ module.exports = ext.register("ext/splitview/splitview", { return; // tabs can be merged into and unmerged from a splitview by clicking a // tab while holding shift - ret = !Splits.mutate(split, page); - this.save(); + console.log("is clone?",apf.isTrue(page.$doc.getNode().getAttribute("clone"))); + if (apf.isTrue(page.$doc.getNode().getAttribute("clone"))) { + tabs.remove(page, null, true); + ret = false; + } + else { + ret = !Splits.mutate(split, page); + this.save(); + } return ret; } }, @@ -437,7 +458,7 @@ module.exports = ext.register("ext/splitview/splitview", { editor.show(); return; } - + Splits.show(split); mnuSplitAlign.setAttribute("checked", split.gridLayout == "3rows"); @@ -481,14 +502,8 @@ module.exports = ext.register("ext/splitview/splitview", { if (!split) return; - var fake = split.clone; delete split.clone; - // use setTimeout(..., 0), or else we go into an infinite loop. There are - // three or more use cases to end a cloneView, this function is just one - // of 'em. - setTimeout(function() { - tabEditors.remove(fake, null, true); - }); + apf.xmldb.setAttribute(page.$doc.getNode(), "clone", false); }, getCloneView: function(page) { @@ -504,7 +519,7 @@ module.exports = ext.register("ext/splitview/splitview", { }, save: function() { - if (!Settings.model) + if (!Settings.model || restoring) return; var node = apf.createNodeFromXpath(Settings.model.data, "splits"); @@ -528,47 +543,115 @@ module.exports = ext.register("ext/splitview/splitview", { }, restore: function(settings) { - return; // no tabs open... don't bother ;) var tabs = tabEditors; if (tabs.getPages().length <= 1) return; - var nodes = settings.selectNodes("splits/split"); + var nodes; + var splits = []; + if (apf.isArray(settings)) { + splits = Splits.get(); + //console.log('restoring from queue',splits,splits.map(function(split){return split.pairs.length;}), settings.map(function(n) {return n.xml;})); + nodes = settings; + } + else { + nodes = settings.selectNodes("splits/split"); + } + if (!nodes || !nodes.length) return; + restoring = true; var activePage = tabs.getPage(); - var i, l, j, l2, ids, active, page, pages, pageSet, gridLayout; - for (i = 0, l = nodes.length; i < l; ++i) { - ids = nodes[i].getAttribute("pages").split(","); - - pages = []; - pageSet = false; - gridLayout = nodes[i].getAttribute("layout") || null; + + var node, ids, j, l2, id, dupes, hasClone, split, page, editor, active; + for (var i = nodes.length - 1; i >= 0; --i) { + node = nodes.pop(); + ids = node.getAttribute("pages").split(","); + + hasClone = false; + dupes = {}; for (j = 0, l2 = ids.length; j < l2; ++j) { - page = tabs.getPage(ids[j]); - if (page) { - if (!pageSet) { - tabs.set(page); - pageSet = true; + id = ids[j]; + if (!dupes[id]) { + dupes[id] = 1; + } + else { + dupes[id]++; + hasClone = id; + } + } + + ids = Object.keys(dupes); + l2 = ids.length + if (l2 < 2) + continue; + + if (hasClone) { + page = tabs.getPage(hasClone); + if (!page) + continue; + if (!page.$doc.acesession) { + if (restoreQueue.indexOf(node) == -1) + restoreQueue.push(node); + continue; + } + else { + split = this.getCloneView(page); + if (!split) { + //console.log("no split???", l2, split, tabs.getPage(hasClone)); + if (restoreQueue.indexOf(node) == -1) + restoreQueue.push(node); + continue; } - Splits.mutate(null, page); } } - if (gridLayout) - Splits.update(null, gridLayout); + else { + split = { + pairs: [], + gridLayout: node.getAttribute("layout") || null + }; + } + + split.activePage = parseInt(node.getAttribute("activepage"), 10) + if (split.activePage < 0) + split.activePage = 0; - Splits.setActivePage(null, parseInt(nodes[i].getAttribute("activepage"), 10)); + //console.log("building split for ",ids); + for (j = 0; j < l2; ++j) { + id = ids[j]; + //console.log("continuing",id, id==hasClone, split.pairs.length); + if (id == hasClone) + continue; + page = tabs.getPage(id); + editor = Splits.getEditor(split, page); + split.pairs.push({ + page: page, + editor: editor, + activePage: activePage + }); + } - if (apf.isTrue(nodes[i].getAttribute("active"))) - active = Splits.getActive(); + if (apf.isTrue(node.getAttribute("active"))) + active = split; + //console.log("getting here for split",ids); + if (splits.indexOf(split) == -1) + splits.push(split); } + //console.log("got splits:",splits); + Splits.set(splits); - if (!active || Splits.indexOf(active, activePage) == -1) + if (!active || Splits.indexOf(active, activePage) == -1) { tabs.set(activePage); - else + } + else { + Splits.update(active); Splits.show(active); + } + + if (!restoreQueue.length) + restoring = false; }, isSupportedEditor: function() { From 30680ee9418c4a28f5060c4208da1b56e84be81f Mon Sep 17 00:00:00 2001 From: mikedeboer Date: Mon, 13 Feb 2012 20:45:46 +0100 Subject: [PATCH 60/69] rewrite of clone view implementation --- client/ext/splitview/splits.js | 31 ++++++++++++++++++++----------- client/ext/splitview/splitview.js | 26 ++++++++++++++++---------- 2 files changed, 36 insertions(+), 21 deletions(-) diff --git a/client/ext/splitview/splits.js b/client/ext/splitview/splits.js index 2510ab152ba..dd23ab10f24 100644 --- a/client/ext/splitview/splits.js +++ b/client/ext/splitview/splits.js @@ -116,7 +116,7 @@ exports.create = function(page, gridLayout) { exports.set = function(splits) { if (!apf.isArray(splits)) return; - + console.log("SPLITS:",splits); Splits = [].concat(splits); for (var split, i = 0, l = Splits.length; i < l; ++i) { split = Splits[i]; @@ -150,7 +150,7 @@ exports.show = function(split) { split.pairs[i].editor.show(); if (split.pairs[i].editor.$editor.onScrollLeftChange) split.pairs[i].editor.$editor.onScrollLeftChange(); - if (!(split.clone && split.pairs[i].page === split.clone)) + //if (!(split.clone && split.pairs[i].page === split.clone)) exports.consolidateEditorSession(split.pairs[i].page, split.pairs[i].editor); } @@ -241,7 +241,7 @@ exports.update = function(split, gridLayout) { if (split === ActiveSplit) { for (var i = 0, l = split.pairs.length; i < l; ++i) { split.pairs[i].page.$activateButton(); - if (!(split.clone && split.pairs[i].page === split.clone)) + //if (!(split.clone && split.pairs[i].page === split.clone)) exports.consolidateEditorSession(split.pairs[i].page, split.pairs[i].editor); } } @@ -258,7 +258,7 @@ exports.mutate = function(split, page, type) { // Remove an editor from the split view if (pairIdx > -1) { - if (split.clone && split.clone === page) + if (exports.isClone(split, page)) SplitView.endCloneView(page); var editor = split.pairs[pairIdx].editor; @@ -295,8 +295,8 @@ exports.mutate = function(split, page, type) { split = this.create(activePage); } if (!split.clone && type == "clone") - split.clone = page; - + split.clone = true; + var editorToUse = this.getEditor(split, page); if (!editorToUse) throw new Error("Splitview fatal error: no editor available to use."); @@ -306,7 +306,7 @@ exports.mutate = function(split, page, type) { editor: editorToUse }); - if (!(split.clone && page === split.clone)) + //if (!(split.clone && page === split.clone)) exports.consolidateEditorSession(page, editorToUse); split.zManager.set(editorToUse.$ext); @@ -319,7 +319,7 @@ exports.mutate = function(split, page, type) { exports.getEditor = function(split, page) { var editorToUse; var clones = createEditorClones.call(this, page.$editor.amlEditor); - if (split.clone && page === split.clone) { + if (split.clone && exports.isClone(page)) { editorToUse = EditorClones.cloneEditor; } else { @@ -398,6 +398,16 @@ exports.indexOf = function(split, obj) { return -1; }; +exports.isClone = function(split, page) { + if (!split.clone) + return false; + var id = page.id; + for (var i = 0, l = split.pairs.length; i < l; ++i) { + if (split.pairs[i].page !== page && split.pairs[i].page.id == id) + return true; + } + return false; +}; exports.getCloneEditor = function(page) { if (page && page.$editor.amlEditor) @@ -538,13 +548,12 @@ function onEditorFocus(editor) { splits.forEach(function(split) { var activePage = split.pairs[exports.indexOf(split, editor)].page; - var isClone = !!split.clone && split.clone.id; for (var page, session, i = 0, l = split.pairs.length; i < l; ++i) { page = split.pairs[i].page; session = exports.getEditorSession(page); if (page === activePage) { // for clone views, the UndoManagers need to be swapped. - if (session && isClone && page.id == isClone) + if (session && exports.isClone(split, page)) session.setUndoManager(page.$at); if (split.activePage !== i) { split.activePage = i; @@ -554,7 +563,7 @@ function onEditorFocus(editor) { } else { // for clone views, the UndoManagers need to be swapped. - if (session && isClone && page.id == isClone) + if (session && exports.isClone(split, page)) session.setUndoManager(CloneUndoManager); apf.setStyleClass(page.$button, InactiveClass, [ActiveClass]); } diff --git a/client/ext/splitview/splitview.js b/client/ext/splitview/splitview.js index aa0b950f6d4..c76ad1381db 100644 --- a/client/ext/splitview/splitview.js +++ b/client/ext/splitview/splitview.js @@ -60,7 +60,7 @@ module.exports = ext.register("ext/splitview/splitview", { if (this.checked) _self.startCloneView(tabs.contextPage); else - _self.endCloneView(tabs.contextPage); + tabs.contextPage.close(); } })) ), @@ -202,6 +202,8 @@ module.exports = ext.register("ext/splitview/splitview", { if (!origPage) return; + + //Splits.consolidateEditorSession(origPage, origPage.$editor.amlEditor); page.$doc = origPage.$doc; page.setAttribute("actiontracker", origPage.$at); page.$at = origPage.$at; @@ -212,7 +214,7 @@ module.exports = ext.register("ext/splitview/splitview", { cont(); function cont() { - tabs.set(origPage); + //tabs.set(origPage); var editor = Splits.getCloneEditor(page); @@ -227,24 +229,29 @@ module.exports = ext.register("ext/splitview/splitview", { editor.setProperty("value", page.acesession); var split = Splits.create(origPage); - split.clone = page; + split.clone = true; split.pairs.push({ page: page, - editor: Splits.getEditor(split, page) + editor: editor//Splits.getEditor(split, page) }); - Splits.update(split); + Splits.consolidateEditorSession(page, editor); page.addEventListener("DOMNodeRemovedFromDocument", function() { _self.endCloneView(page); }); origPage.addEventListener("DOMNodeRemovedFromDocument", function() { - _self.endCloneView(page); + _self.endCloneView(origPage); }); - if (restoreQueue.length) + console.log("got restore lenght?",restoreQueue.length); + if (restoreQueue.length) { _self.restore(restoreQueue); - //_self.save(); + } + else { + Splits.show(split); + _self.save(); + } } }); @@ -549,9 +556,8 @@ module.exports = ext.register("ext/splitview/splitview", { return; var nodes; - var splits = []; + var splits = Splits.get(); if (apf.isArray(settings)) { - splits = Splits.get(); //console.log('restoring from queue',splits,splits.map(function(split){return split.pairs.length;}), settings.map(function(n) {return n.xml;})); nodes = settings; } From da27e6a1133d527fdcdf9547e67c786f00073fbe Mon Sep 17 00:00:00 2001 From: mikedeboer Date: Mon, 13 Feb 2012 22:08:16 +0100 Subject: [PATCH 61/69] new splitter styles and removed log messages --- client/ext/splitview/splits.js | 19 ++++--- client/ext/splitview/splitview.js | 27 ++++------ client/style/images/splitter_dark_grab.png | Bin 999 -> 2901 bytes client/style/skins.xml | 57 +++++++++++++-------- 4 files changed, 55 insertions(+), 48 deletions(-) diff --git a/client/ext/splitview/splits.js b/client/ext/splitview/splits.js index dd23ab10f24..9ec7f6d67b2 100644 --- a/client/ext/splitview/splits.js +++ b/client/ext/splitview/splits.js @@ -116,7 +116,7 @@ exports.create = function(page, gridLayout) { exports.set = function(splits) { if (!apf.isArray(splits)) return; - console.log("SPLITS:",splits); + //console.log("SPLITS:",splits); Splits = [].concat(splits); for (var split, i = 0, l = Splits.length; i < l; ++i) { split = Splits[i]; @@ -144,7 +144,7 @@ exports.show = function(split) { for (i = 0, l = aSplit.pairs.length; i < l; ++i) aSplit.pairs[i].page.$deactivateButton(); }); - //console.log("pages",split.pairs.map(function(pair){return pair.page.name;})); + for (i = 0, l = split.pairs.length; i < l; ++i) { split.pairs[i].page.$activateButton(); split.pairs[i].editor.show(); @@ -191,8 +191,6 @@ exports.update = function(split, gridLayout) { split.gridLayout = gridLayout; // destroy the split view if it contains NOT more than 1 editor. - //console.log("number of pages in split view:", split.pairs.length,"vs editors:", - // split.editors.length,page.name,split.pages.map(function(page){return page.name})); if (split.pairs.length === 1) { var editor = page.$editor.amlEditor; if (EditorClones[editor.localName]) { @@ -227,7 +225,7 @@ exports.update = function(split, gridLayout) { // sort the editors and pages before being added to the grid sortEditorsAndPages(split); - //console.log("split editors:", split.pairs.length, split.pairs.map(function(pair) { return pair.editor.id; })); + Grids.update(gridLayout, split); // make sure visual styles are OK setSplitViewStyles(split); @@ -404,7 +402,7 @@ exports.isClone = function(split, page) { var id = page.id; for (var i = 0, l = split.pairs.length; i < l; ++i) { if (split.pairs[i].page !== page && split.pairs[i].page.id == id) - return true; + return split.pairs[i]; } return false; }; @@ -529,7 +527,6 @@ function addEditorListeners(editor) { function removeEditorListeners(editor) { if (!editor.$splitListener) return; - //console.log("removing event listeners!!"); editor.removeEventListener("focus", editor.$splitListener); delete editor.$splitListener; } @@ -551,9 +548,12 @@ function onEditorFocus(editor) { for (var page, session, i = 0, l = split.pairs.length; i < l; ++i) { page = split.pairs[i].page; session = exports.getEditorSession(page); + var isClone = session && exports.isClone(split, page); + if (isClone) + isClone = exports.getEditorSession(isClone); if (page === activePage) { // for clone views, the UndoManagers need to be swapped. - if (session && exports.isClone(split, page)) + if (isClone && isClone.getUndoManager() !== page.$at) session.setUndoManager(page.$at); if (split.activePage !== i) { split.activePage = i; @@ -563,7 +563,7 @@ function onEditorFocus(editor) { } else { // for clone views, the UndoManagers need to be swapped. - if (session && exports.isClone(split, page)) + if (isClone && isClone.getUndoManager() !== CloneUndoManager) session.setUndoManager(CloneUndoManager); apf.setStyleClass(page.$button, InactiveClass, [ActiveClass]); } @@ -625,7 +625,6 @@ function correctQuickSearchDialog(e) { var maxRight = parentDims.width - 380; var right = parentDims.width - editorPos[0] - editorDims.width + 30; var top = editorPos[1]; - //console.log("editorPos", editorPos,"editorDims",JSON.stringify(editorDims),"parentDims",JSON.stringify(parentDims),"right",right,"top",top); var to = Math.max(top, 0); return { right: Math.max(Math.min(right, maxRight), minRight), diff --git a/client/ext/splitview/splitview.js b/client/ext/splitview/splitview.js index c76ad1381db..aa5c7a2a3ee 100644 --- a/client/ext/splitview/splitview.js +++ b/client/ext/splitview/splitview.js @@ -202,7 +202,6 @@ module.exports = ext.register("ext/splitview/splitview", { if (!origPage) return; - //Splits.consolidateEditorSession(origPage, origPage.$editor.amlEditor); page.$doc = origPage.$doc; page.setAttribute("actiontracker", origPage.$at); @@ -214,8 +213,6 @@ module.exports = ext.register("ext/splitview/splitview", { cont(); function cont() { - //tabs.set(origPage); - var editor = Splits.getCloneEditor(page); page.acesession = new EditSession(page.$doc.acedoc); @@ -232,7 +229,7 @@ module.exports = ext.register("ext/splitview/splitview", { split.clone = true; split.pairs.push({ page: page, - editor: editor//Splits.getEditor(split, page) + editor: editor }); Splits.consolidateEditorSession(page, editor); @@ -244,12 +241,12 @@ module.exports = ext.register("ext/splitview/splitview", { _self.endCloneView(origPage); }); - console.log("got restore lenght?",restoreQueue.length); if (restoreQueue.length) { _self.restore(restoreQueue); } else { Splits.show(split); + mnuCloneView.setAttribute("checked", true); _self.save(); } } @@ -381,7 +378,7 @@ module.exports = ext.register("ext/splitview/splitview", { return; // tabs can be merged into and unmerged from a splitview by clicking a // tab while holding shift - console.log("is clone?",apf.isTrue(page.$doc.getNode().getAttribute("clone"))); + //console.log("is clone?",apf.isTrue(page.$doc.getNode().getAttribute("clone"))); if (apf.isTrue(page.$doc.getNode().getAttribute("clone"))) { tabs.remove(page, null, true); ret = false; @@ -434,7 +431,6 @@ module.exports = ext.register("ext/splitview/splitview", { // hide the previous split view if (previous && previous.$model) { var oldSplit = Splits.get(previous)[0]; - //console.log("got old split?",oldSplit); if (oldSplit && (!split || oldSplit.gridLayout != split.gridLayout)) Splits.hide(oldSplit); } @@ -557,13 +553,10 @@ module.exports = ext.register("ext/splitview/splitview", { var nodes; var splits = Splits.get(); - if (apf.isArray(settings)) { - //console.log('restoring from queue',splits,splits.map(function(split){return split.pairs.length;}), settings.map(function(n) {return n.xml;})); + if (apf.isArray(settings)) nodes = settings; - } - else { + else nodes = settings.selectNodes("splits/split"); - } if (!nodes || !nodes.length) return; @@ -590,7 +583,7 @@ module.exports = ext.register("ext/splitview/splitview", { } ids = Object.keys(dupes); - l2 = ids.length + l2 = ids.length; if (l2 < 2) continue; @@ -606,7 +599,6 @@ module.exports = ext.register("ext/splitview/splitview", { else { split = this.getCloneView(page); if (!split) { - //console.log("no split???", l2, split, tabs.getPage(hasClone)); if (restoreQueue.indexOf(node) == -1) restoreQueue.push(node); continue; @@ -624,10 +616,8 @@ module.exports = ext.register("ext/splitview/splitview", { if (split.activePage < 0) split.activePage = 0; - //console.log("building split for ",ids); for (j = 0; j < l2; ++j) { id = ids[j]; - //console.log("continuing",id, id==hasClone, split.pairs.length); if (id == hasClone) continue; page = tabs.getPage(id); @@ -641,11 +631,9 @@ module.exports = ext.register("ext/splitview/splitview", { if (apf.isTrue(node.getAttribute("active"))) active = split; - //console.log("getting here for split",ids); if (splits.indexOf(split) == -1) splits.push(split); } - //console.log("got splits:",splits); Splits.set(splits); if (!active || Splits.indexOf(active, activePage) == -1) { @@ -654,6 +642,9 @@ module.exports = ext.register("ext/splitview/splitview", { else { Splits.update(active); Splits.show(active); + mnuSplitAlign.setAttribute("checked", active.gridLayout == "3rows"); + if (active.clone) + mnuCloneView.setAttribute("checked", true); } if (!restoreQueue.length) diff --git a/client/style/images/splitter_dark_grab.png b/client/style/images/splitter_dark_grab.png index e83208f506d8810b4ffea66d15e23c507e811317..f9e6539284ea7057a11cb79126affb2ac28619ef 100644 GIT binary patch literal 2901 zcmV-b3##;qP)KLZ*U+IBfRsybQWXdwQbLP>6pAqfylh#{fb6;Z(vMMVS~$e@S=j*ftg6;Uhf59&ghTmgWD0l;*T zI709Y^p6lP1rIRMx#05C~cW=H_Aw*bJ-5DT&Z2n+x)QHX^p z00esgV8|mQcmRZ%02D^@S3L16t`O%c004NIvOKvYIYoh62rY33S640`D9%Y2D-rV&neh&#Q1i z007~1e$oCcFS8neI|hJl{-P!B1ZZ9hpmq0)X0i`JwE&>$+E?>%_LC6RbVIkUx0b+_+BaR3cnT7Zv!AJxW zizFb)h!jyGOOZ85F;a?DAXP{m@;!0_IfqH8(HlgRxt7s3}k3K`kFu>>-2Q$QMFfPW!La{h336o>X zu_CMttHv6zR;&ZNiS=X8v3CR#fknUxHUxJ0uoBa_M6WNWeqIg~6QE69c9o#eyhGvpiOA@W-aonk<7r1(?fC{oI5N*U!4 zfg=2N-7=cNnjjOr{yriy6mMFgG#l znCF=fnQv8CDz++o6_Lscl}eQ+l^ZHARH>?_s@|##Rr6KLRFA1%Q+=*RRWnoLsR`7U zt5vFIcfW3@?wFpwUVxrVZ>QdQz32KIeJ}k~{cZZE^+ya? z2D1z#2HOnI7(B%_ac?{wFUQ;QQA1tBKtrWrm0_3Rgps+?Jfqb{jYbcQX~taRB;#$y zZN{S}1|}gUOHJxc?wV3fxuz+mJ4`!F$IZ;mqRrNsHJd##*D~ju=bP7?-?v~|cv>vB zsJ6IeNwVZxrdjT`yl#bBIa#GxRa#xMMy;K#CDyyGyQdMSxlWT#tDe?p!?5wT$+oGt z8L;Kp2HUQ-ZMJ=3XJQv;x5ci*?vuTfeY$;({XGW_huIFR9a(?@3)XSs8O^N5RyOM=TTmp(3=8^+zpz2r)C z^>JO{deZfso3oq3?Wo(Y?l$ge?uXo;%ru`Vo>?<<(8I_>;8Eq#KMS9gFl*neeosSB zfoHYnBQIkwkyowPu(zdms`p{<7e4kra-ZWq<2*OsGTvEV%s0Td$hXT+!*8Bnh2KMe zBmZRodjHV?r+_5^X9J0WL4jKW`}lf%A-|44I@@LTvf1rHjG(ze6+w@Jt%Bvjts!X0 z?2xS?_ve_-kiKB_KiJlZ$9G`c^=E@oNG)mWWaNo-3TIW8)$Hg0Ub-~8?KhvJ>$ z3*&nim@mj(aCxE5!t{lw7O5^0EIO7zOo&c6l<+|iDySBWCGrz@C5{St!X3hAA}`T4 z(TLbXTq+(;@<=L8dXnssyft|w#WSTW<++3>sgS%(4NTpeI-VAqb|7ssJvzNHgOZVu zaYCvgO_R1~>SyL=cFU|~g|hy|Zi}}s9+d~lYqOB71z9Z$wnC=pR9Yz4DhIM>Wmjgu z&56o6maCpC&F##y%G;1PobR9i?GnNg;gYtchD%p19a!eQtZF&3JaKv33gZ<8D~47E ztUS1iwkmDaPpj=$m#%)jCVEY4fnLGNg2A-`YwHVD3gv};>)hAvT~AmqS>Lr``i7kw zJ{5_It`yrBmlc25DBO7E8;5VoznR>Ww5hAaxn$2~(q`%A-YuS64wkBy=9dm`4cXeX z4c}I@?e+FW+b@^RDBHV(wnMq2zdX3SWv9u`%{xC-q*U}&`cyXV(%rRT*Z6MH?i+i& z_B8C(+grT%{XWUQ+f@NoP1R=AW&26{v-dx)iK^-Nmiuj8txj!m?Z*Ss1N{dh4z}01 z)YTo*JycSU)+_5r4#yw9{+;i4Ee$peRgIj+;v;ZGdF1K$3E%e~4LaI(jC-u%2h$&R z9cLXcYC@Xwnns&bn)_Q~Te?roKGD|d-g^8;+aC{{G(1^(O7m37Y1-+6)01cN&y1aw zoqc{T`P^XJqPBbIW6s}d4{z_f5Om?vMgNQEJG?v2T=KYd^0M3I6IZxbny)%vZR&LD zJpPl@Psh8QyPB@KTx+@RdcC!KX7}kEo;S|j^u2lU7XQ}Oo;f|;z4Ll+_r>@1-xl3| zawq-H%e&ckC+@AhPrP6BKT#_XdT7&;F71j}Joy zkC~6lh7E@6o;W@^IpRNZ{ptLtL(gQ-CY~4mqW;US7Zxvm_|@yz&e53Bp_lTPlfP|z zrTyx_>lv@x#=^!PzR7qqF<$gm`|ZJZ+;<)Cqu&ot2z=0000WV@Og>004R=004l4008;_004mL004C`008P>0026e000+nl3&F} z0001hNkl{39<*Ho!?+tv^c8dzn7-{18@nJiOeUji z^Rm|G>eyUykW2bp>x7#c%8f?B@u+7V`Sv4Z&HLB^-INu3cr4OTy&l&Z9?R5FE>DBu zqsIQ#=>8R*p6ECjTWSmjAGvheeLB{k#ho$wB<|()Y$^Ow`Lw^kU5BvK{<^y!yE5~5 z=VSHo=fV32Ba>ja`upf;`&OxW?fgM)?Nw)U^U3&|v(I8b;MI`}%!inMqk4UEa_eQM mvpl}{eCy&1W$k{AIem%QOQDU-=&#q&)63@;^|x~mpZ)=${yy{o diff --git a/client/style/skins.xml b/client/style/skins.xml index d6ed37ed575..02df1f95927 100644 --- a/client/style/skins.xml +++ b/client/style/skins.xml @@ -6999,7 +6999,7 @@ .darksplitter { position : relative; z-index : 90000; - background-color: #e4e4e4; + background-color: #e8e8e8; display : -moz-box; display : -webkit-box; @@ -7017,10 +7017,6 @@ cursor : ns-resize; } - .darksplitterMoving { - background-color : #8c8c8c; - } - .darksplitterRealtime { background-image : url(images/spacer.gif); } @@ -7033,25 +7029,35 @@ } .darksplitter.vertical { - border-left: 1px solid #c7c7c7; + border-right: 1px solid #e8e8e8; vertical-align: middle; width: 6px; + } + + .darksplitterMoving.vertical, + .darksplitter.vertical:hover { + border-right: 1px solid #d7d7d7; - background: -moz-linear-gradient(left, #dbdbdb 0%, #e8e8e8 100%); /* FF3.6+ */ - background: -webkit-gradient(linear, left top, right top, color-stop(0%,#d6d5d5), color-stop(100%,#e8e8e8)); /* Chrome,Safari4+ */ - background: -webkit-linear-gradient(left, #dbdbdb 0%,#e8e8e8 100%); /* Chrome10+,Safari5.1+ */ - background: -o-linear-gradient(left, #dbdbdb 0%,#e8e8e8 100%); /* Opera 11.10+ */ - background: -ms-linear-gradient(left, #dbdbdb 0%,#e8e8e8 100%); /* IE10+ */ - background: linear-gradient(left, #dbdbdb 0%,#e8e8e8 100%); /* W3C */ + background: -moz-linear-gradient(left, #e8e8e8 50%, #f0f0f0 100%); /* FF3.6+ */ + background: -webkit-gradient(linear, left top, right top, color-stop(50%,#e8e8e8), color-stop(100%,#f0f0f0)); /* Chrome,Safari4+ */ + background: -webkit-linear-gradient(left, #e8e8e8 50%,#f0f0f0 100%); /* Chrome10+,Safari5.1+ */ + background: -o-linear-gradient(left, #e8e8e8 50%,#f0f0f0 100%); /* Opera 11.10+ */ + background: -ms-linear-gradient(left, #e8e8e8 50%,#f0f0f0 100%); /* IE10+ */ + background: linear-gradient(left, #e8e8e8 50%,#f0f0f0 100%); /* W3C */ } - + .darksplitter.vertical span { width: 3px; height: 11px; background-position: 0 -3px; margin-top: 49%; - margin-left: 1px; + margin-left: 2px; + } + + .darksplitterMoving.vertical span, + .darksplitter.vertical:hover span { + background-position: 0 -18px; } .darksplitter.horizontal { @@ -7060,12 +7066,18 @@ height: 6px; border-top: 1px solid #c7c7c7; - background: -moz-linear-gradient(top, #d6d5d5 0%, #e8e8e8 100%); /* FF3.6+ */ - background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#d6d5d5), color-stop(100%,#e8e8e8)); /* Chrome,Safari4+ */ - background: -webkit-linear-gradient(top, #d6d5d5 0%,#e8e8e8 100%); /* Chrome10+,Safari5.1+ */ - background: -o-linear-gradient(top, #d6d5d5 0%,#e8e8e8 100%); /* Opera 11.10+ */ - background: -ms-linear-gradient(top, #d6d5d5 0%,#e8e8e8 100%); /* IE10+ */ - background: linear-gradient(top, #d6d5d5 0%,#e8e8e8 100%); /* W3C */ + } + + .darksplitterMoving.horizontal, + .darksplitter.horizontal:hover { + border-bottom: 1px solid #d7d7d7; + + background: -moz-linear-gradient(top, #e8e8e8 50%, #f2f2f2 100%); /* FF3.6+ */ + background: -webkit-gradient(linear, left top, left bottom, color-stop(50%,#e8e8e8), color-stop(100%,#f2f2f2)); /* Chrome,Safari4+ */ + background: -webkit-linear-gradient(top, #e8e8e8 50%,#f2f2f2 100%); /* Chrome10+,Safari5.1+ */ + background: -o-linear-gradient(top, #e8e8e8 50%,#f2f2f2 100%); /* Opera 11.10+ */ + background: -ms-linear-gradient(top, #e8e8e8 50%,#f2f2f2 100%); /* IE10+ */ + background: linear-gradient(top, #e8e8e8 50%,#f2f2f2 100%); /* W3C */ } .darksplitter.horizontal span { @@ -7074,6 +7086,11 @@ margin-left: 49%; margin-top: 1px; } + + .darksplitterMoving.horizontal span, + .darksplitter.horizontal:hover span { + background-position: 0 -15px; + } ]]> From 3f0694d5bb57160b30e7f8d4e2b718c6719f0e24 Mon Sep 17 00:00:00 2001 From: mikedeboer Date: Tue, 14 Feb 2012 10:05:03 +0100 Subject: [PATCH 62/69] added bottom border for horizontal split --- client/style/skins.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/client/style/skins.xml b/client/style/skins.xml index 02df1f95927..361c3ed6e20 100644 --- a/client/style/skins.xml +++ b/client/style/skins.xml @@ -7066,6 +7066,7 @@ height: 6px; border-top: 1px solid #c7c7c7; + border-bottom: 1px solid #e8e8e8; } .darksplitterMoving.horizontal, From ae169e02297cdd33834dfd3bb0a1f2a511f83eca Mon Sep 17 00:00:00 2001 From: mikedeboer Date: Tue, 14 Feb 2012 15:02:26 +0100 Subject: [PATCH 63/69] fixed clone view creation and splitter skin style --- client/ext/splitview/splits.js | 24 +++++++------- client/ext/splitview/splitview.js | 52 +++++++++++++++++++++++++++++-- client/style/skins.xml | 3 +- 3 files changed, 63 insertions(+), 16 deletions(-) diff --git a/client/ext/splitview/splits.js b/client/ext/splitview/splits.js index 9ec7f6d67b2..e5f0ea2efb0 100644 --- a/client/ext/splitview/splits.js +++ b/client/ext/splitview/splits.js @@ -116,7 +116,6 @@ exports.create = function(page, gridLayout) { exports.set = function(splits) { if (!apf.isArray(splits)) return; - //console.log("SPLITS:",splits); Splits = [].concat(splits); for (var split, i = 0, l = Splits.length; i < l; ++i) { split = Splits[i]; @@ -131,10 +130,10 @@ exports.show = function(split) { if (!split || split === ActiveSplit) return this; - this.update(split); if (ActiveSplit) this.hide(ActiveSplit, ActiveSplit.gridLayout == split.gridLayout); - Grids.show(split.gridLayout); + this.update(split); + //Grids.show(split.gridLayout); var i, l; // maintain page button styles @@ -144,14 +143,12 @@ exports.show = function(split) { for (i = 0, l = aSplit.pairs.length; i < l; ++i) aSplit.pairs[i].page.$deactivateButton(); }); - for (i = 0, l = split.pairs.length; i < l; ++i) { split.pairs[i].page.$activateButton(); split.pairs[i].editor.show(); if (split.pairs[i].editor.$editor.onScrollLeftChange) split.pairs[i].editor.$editor.onScrollLeftChange(); - //if (!(split.clone && split.pairs[i].page === split.clone)) - exports.consolidateEditorSession(split.pairs[i].page, split.pairs[i].editor); + exports.consolidateEditorSession(split.pairs[i].page, split.pairs[i].editor); } ActiveSplit = split; @@ -239,8 +236,7 @@ exports.update = function(split, gridLayout) { if (split === ActiveSplit) { for (var i = 0, l = split.pairs.length; i < l; ++i) { split.pairs[i].page.$activateButton(); - //if (!(split.clone && split.pairs[i].page === split.clone)) - exports.consolidateEditorSession(split.pairs[i].page, split.pairs[i].editor); + exports.consolidateEditorSession(split.pairs[i].page, split.pairs[i].editor); } } @@ -268,7 +264,6 @@ exports.mutate = function(split, page, type) { //removeEditorListeners(editor); editor.hide(); apf.document.body.appendChild(editor); - page.$deactivateButton(); clearSplitViewStyles(page); editor.hide(); @@ -506,8 +501,11 @@ exports.consolidateEditorSession = function(page, editor) { } if (!editor) console.trace(); - editor.setAttribute("model", page.$model); - editor.setAttribute("actiontracker", page.$at); + + if (editor.model !== page.$model) + editor.setAttribute("model", page.$model); + if (editor.actiontracker !== page.$at) + editor.setAttribute("actiontracker", page.$at); if (editor.value !== session) editor.setProperty("value", session); }; @@ -553,7 +551,7 @@ function onEditorFocus(editor) { isClone = exports.getEditorSession(isClone); if (page === activePage) { // for clone views, the UndoManagers need to be swapped. - if (isClone && isClone.getUndoManager() !== page.$at) + if (isClone && editor.getUndoManager() !== page.$at) session.setUndoManager(page.$at); if (split.activePage !== i) { split.activePage = i; @@ -563,7 +561,7 @@ function onEditorFocus(editor) { } else { // for clone views, the UndoManagers need to be swapped. - if (isClone && isClone.getUndoManager() !== CloneUndoManager) + if (isClone && editor.getUndoManager() !== CloneUndoManager) session.setUndoManager(CloneUndoManager); apf.setStyleClass(page.$button, InactiveClass, [ActiveClass]); } diff --git a/client/ext/splitview/splitview.js b/client/ext/splitview/splitview.js index aa5c7a2a3ee..48a002259d9 100644 --- a/client/ext/splitview/splitview.js +++ b/client/ext/splitview/splitview.js @@ -206,6 +206,31 @@ module.exports = ext.register("ext/splitview/splitview", { page.$doc = origPage.$doc; page.setAttribute("actiontracker", origPage.$at); page.$at = origPage.$at; + + // find the settings node that corresponds with the clone view + // that is being constructed right now + var settings, pages, indices, idx; + if (Settings.model.data) { + var nodes = Settings.model.data.selectNodes("splits/split"); + for (i = 0, l = nodes.length; i < l; ++i) { + pages = nodes[i] && nodes[i].getAttribute("pages"); + if (!pages) + continue; + pages = pages.split(","); + indices = []; + idx = pages.indexOf(origPage.id); + while (idx != -1) { + indices.push(idx); + idx = pages.indexOf(origPage.id, idx + 1); + } + if (indices.length < 2) + continue; + settings = nodes[i]; + break; + } + } + if (settings && apf.isTrue(settings.getAttribute("active"))) + tabs.set(origPage); if (!page.$doc.acedoc) page.$doc.addEventListener("init", cont); @@ -213,6 +238,8 @@ module.exports = ext.register("ext/splitview/splitview", { cont(); function cont() { + restoring = true; + var editor = Splits.getCloneEditor(page); page.acesession = new EditSession(page.$doc.acedoc); @@ -231,6 +258,23 @@ module.exports = ext.register("ext/splitview/splitview", { page: page, editor: editor }); + + if (settings) { + var addPage; + for (i = 0, l = pages.length; i < l; ++i) { + if (pages[i] == origPage.id) + continue; + addPage = tabs.getPage(pages[i]); + if (!addPage || Splits.indexOf(split, addPage) > -1) + continue; + split.pairs.splice(i, 0, { + page: addPage, + editor: Splits.getEditor(split, addPage) + }); + } + split.activePage = parseInt(settings.getAttribute("activepage"), 10) || 0; + split.gridLayout = settings.getAttribute("layout"); + } Splits.consolidateEditorSession(page, editor); page.addEventListener("DOMNodeRemovedFromDocument", function() { @@ -422,6 +466,8 @@ module.exports = ext.register("ext/splitview/splitview", { }, updateSplitView: function(previous, next) { + if (restoring) + return; var editor; var doc = next.$doc; var at = next.$at; @@ -621,11 +667,12 @@ module.exports = ext.register("ext/splitview/splitview", { if (id == hasClone) continue; page = tabs.getPage(id); + if (Splits.indexOf(split, page) > -1) + continue; editor = Splits.getEditor(split, page); split.pairs.push({ page: page, - editor: editor, - activePage: activePage + editor: editor }); } @@ -640,6 +687,7 @@ module.exports = ext.register("ext/splitview/splitview", { tabs.set(activePage); } else { + tabs.set(active.pairs[0].page); Splits.update(active); Splits.show(active); mnuSplitAlign.setAttribute("checked", active.gridLayout == "3rows"); diff --git a/client/style/skins.xml b/client/style/skins.xml index 361c3ed6e20..96b06859129 100644 --- a/client/style/skins.xml +++ b/client/style/skins.xml @@ -7030,6 +7030,7 @@ .darksplitter.vertical { border-right: 1px solid #e8e8e8; + border-left: 1px solid #d7d7d7; vertical-align: middle; width: 6px; @@ -7052,7 +7053,7 @@ height: 11px; background-position: 0 -3px; margin-top: 49%; - margin-left: 2px; + margin-left: 1px; } .darksplitterMoving.vertical span, From a73da333960a704a0f8d7f976b1d58ba504e1d1a Mon Sep 17 00:00:00 2001 From: mikedeboer Date: Tue, 14 Feb 2012 16:12:49 +0100 Subject: [PATCH 64/69] fixed several edge cases --- client/ext/splitview/grids.js | 2 +- client/ext/splitview/splits.js | 2 +- client/ext/splitview/splitview.js | 22 +++++++++++----------- client/ext/tabbehaviors/tabbehaviors.js | 1 + 4 files changed, 14 insertions(+), 13 deletions(-) diff --git a/client/ext/splitview/grids.js b/client/ext/splitview/grids.js index b8f855144c6..dbf06af5622 100644 --- a/client/ext/splitview/grids.js +++ b/client/ext/splitview/grids.js @@ -219,7 +219,7 @@ function createNodes(struct, splitters, parent) { else if (nodeName == "splitter") { options.visible = false; options.skin = "darksplitter"; - options.zindex = 1; + options.zindex = 8; } var node = parent.appendChild(new apf[nodeName](options)); diff --git a/client/ext/splitview/splits.js b/client/ext/splitview/splits.js index e5f0ea2efb0..dc9e8b2e3d8 100644 --- a/client/ext/splitview/splits.js +++ b/client/ext/splitview/splits.js @@ -133,7 +133,7 @@ exports.show = function(split) { if (ActiveSplit) this.hide(ActiveSplit, ActiveSplit.gridLayout == split.gridLayout); this.update(split); - //Grids.show(split.gridLayout); + Grids.show(split.gridLayout); var i, l; // maintain page button styles diff --git a/client/ext/splitview/splitview.js b/client/ext/splitview/splitview.js index 48a002259d9..18ab5b7a217 100644 --- a/client/ext/splitview/splitview.js +++ b/client/ext/splitview/splitview.js @@ -238,8 +238,6 @@ module.exports = ext.register("ext/splitview/splitview", { cont(); function cont() { - restoring = true; - var editor = Splits.getCloneEditor(page); page.acesession = new EditSession(page.$doc.acedoc); @@ -466,8 +464,8 @@ module.exports = ext.register("ext/splitview/splitview", { }, updateSplitView: function(previous, next) { - if (restoring) - return; + //if (restoring) + // return; var editor; var doc = next.$doc; var at = next.$at; @@ -667,7 +665,7 @@ module.exports = ext.register("ext/splitview/splitview", { if (id == hasClone) continue; page = tabs.getPage(id); - if (Splits.indexOf(split, page) > -1) + if (!page || Splits.indexOf(split, page) > -1) continue; editor = Splits.getEditor(split, page); split.pairs.push({ @@ -676,18 +674,20 @@ module.exports = ext.register("ext/splitview/splitview", { }); } - if (apf.isTrue(node.getAttribute("active"))) - active = split; - if (splits.indexOf(split) == -1) - splits.push(split); + if (split.pairs.length > 1) { + if (apf.isTrue(node.getAttribute("active"))) + active = split; + if (splits.indexOf(split) == -1) + splits.push(split); + } } Splits.set(splits); if (!active || Splits.indexOf(active, activePage) == -1) { tabs.set(activePage); } - else { - tabs.set(active.pairs[0].page); + else if (active) { + //tabs.set(active.pairs[0].page); Splits.update(active); Splits.show(active); mnuSplitAlign.setAttribute("checked", active.gridLayout == "3rows"); diff --git a/client/ext/tabbehaviors/tabbehaviors.js b/client/ext/tabbehaviors/tabbehaviors.js index 60d1c1ba611..e5e474f1ebc 100644 --- a/client/ext/tabbehaviors/tabbehaviors.js +++ b/client/ext/tabbehaviors/tabbehaviors.js @@ -401,6 +401,7 @@ module.exports = ext.register("ext/tabbehaviors/tabbehaviors", { tabs.appendChild(curr) else tabs.insertBefore(curr, pages[idx]); + tabs.dispatchEvent("reorder", { page: curr }); return false; }, From 6230a101731583e39914f86e2f9037b2d213bcc8 Mon Sep 17 00:00:00 2001 From: mikedeboer Date: Thu, 23 Feb 2012 11:57:13 +0100 Subject: [PATCH 65/69] merge with master complete --- server/cloud9/ide.js | 1 - 1 file changed, 1 deletion(-) diff --git a/server/cloud9/ide.js b/server/cloud9/ide.js index 3c2df64cac9..1099f77f04b 100644 --- a/server/cloud9/ide.js +++ b/server/cloud9/ide.js @@ -143,7 +143,6 @@ Ide.DEFAULT_PLUGINS = [ "ext/jslanguage/jslanguage", "ext/autotest/autotest", "ext/splitview/splitview", - "ext/tabsessions/tabsessions" "ext/tabsessions/tabsessions", "ext/closeconfirmation/closeconfirmation", "ext/codetools/codetools", From 9ac52dc48cafd735969d7568f80662e5999afb3c Mon Sep 17 00:00:00 2001 From: mikedeboer Date: Fri, 6 Apr 2012 11:33:58 +0200 Subject: [PATCH 66/69] fixed syntax error in html.js and splitview stability fixes --- client/ext/html/html.js | 7 +- client/ext/splitview/splitview.js | 158 +++++++++++++++--------------- 2 files changed, 85 insertions(+), 80 deletions(-) diff --git a/client/ext/html/html.js b/client/ext/html/html.js index 0104478313f..ea64bf4a743 100644 --- a/client/ext/html/html.js +++ b/client/ext/html/html.js @@ -30,17 +30,20 @@ module.exports = ext.register("ext/html/html", { hook : function(){ var _self = this; + var tabs = tabEditors; - tabEditors.addEventListener("afterswitch", function(e){ + tabs.addEventListener("afterswitch", function(e){ _self.afterSwitchOrOpen(e.nextPage); }); + ide.addEventListener("afteropenfile", function(e){ // Only listen for event from editors.js if (e.editor && e.node.$model) _self.afterSwitchOrOpen(e.node); }); + ide.addEventListener("updatefile", function(e) { - var page = tabEditors.getPage(e.newPath); + var page = tabs.getPage(e.newPath); if (!page || !page.$active) return; _self.afterSwitchOrOpen(page); diff --git a/client/ext/splitview/splitview.js b/client/ext/splitview/splitview.js index 18ab5b7a217..1bb13c76e22 100644 --- a/client/ext/splitview/splitview.js +++ b/client/ext/splitview/splitview.js @@ -30,7 +30,7 @@ module.exports = ext.register("ext/splitview/splitview", { dev : "Ajax.org", alone : true, type : ext.GENERAL, - + commands : { "mergetableft": {hint: "Add the page on the left of the currently active page to a split view"}, "mergetabright": {hint: "Add the page on the right of the currently active page to a split view"}, @@ -39,15 +39,15 @@ module.exports = ext.register("ext/splitview/splitview", { }, hotitems : [], nodes : [], - + splits : [], - + init : function(){ apf.importCssString(css || ""); - + var _self = this; var tabs = tabEditors; // localize global 'tabEditors' - + var parent = Tabbehaviors.menu; this.nodes.push( parent.appendChild(new apf.divider()), @@ -60,7 +60,7 @@ module.exports = ext.register("ext/splitview/splitview", { if (this.checked) _self.startCloneView(tabs.contextPage); else - tabs.contextPage.close(); + tabs.contextPage.removeNode(); } })) ), @@ -75,20 +75,20 @@ module.exports = ext.register("ext/splitview/splitview", { })) ) ); - + ide.addEventListener("editorswitch", function(e) { // the return value actually does something! return _self.updateSplitView(e.previousPage, e.nextPage); }); - + ide.addEventListener("pageswitch", function(e) { if (!Splits.getActive()) return; _self.save(); - + if (typeof mnuSyntax == "undefined") return; - + var item; var syntax = mnuSyntax; var value = Code.getContentType(e.page.$model.data); @@ -102,35 +102,35 @@ module.exports = ext.register("ext/splitview/splitview", { } } }); - + ide.addEventListener("closefile", function(e) { _self.onFileClose(e); }); - + ide.addEventListener("beforecycletab", function(e) { _self.onCycleTab(e); }); - + function onAccessTabbing(e) { var split = Splits.get(e.page)[0]; return !Splits.isActive(split); } - + ide.addEventListener("beforenexttab", function(e) { return onAccessTabbing(e); }); - + ide.addEventListener("beforeprevioustab", function(e) { return onAccessTabbing(e); }); - + ide.addEventListener("beforeclosetab", function(e) { var split = Splits.get(e.page)[0]; if (!Splits.isActive(split)) return; e.returnValue = split.pairs[split.activePage].page; }); - + ide.addEventListener("correctactivepage", function(e) { var split = Splits.getActive(); var editor = Editors.currentEditor && Editors.currentEditor.amlEditor; @@ -141,18 +141,18 @@ module.exports = ext.register("ext/splitview/splitview", { return; e.returnValue = split.pairs[idx].page; }); - + tabs.addEventListener("tabselectclick", function(e) { return _self.onTabClick(e); }); - + tabs.addEventListener("tabselectmouseup", function(e) { var page = this.$activepage; var split = Splits.get(page)[0]; if (split) Splits.update(split); }); - + tabs.addEventListener("reorder", function(e) { Splits.get(e.page).forEach(function(split) { if (Splits.isActive(split)) @@ -160,7 +160,7 @@ module.exports = ext.register("ext/splitview/splitview", { }); _self.save(); }); - + ide.addEventListener("loadsettings", function(e) { if (!e.model || !e.model.data) return; @@ -171,22 +171,22 @@ module.exports = ext.register("ext/splitview/splitview", { }); }); }); - + ide.addEventListener("activepagemodel", function(e) { var page = tabs.getPage(); var split = Splits.get(page)[0]; if (!split || !Splits.isActive(split)) return; - + e.returnValue = split.pairs[split.activePage || 0].page.$model; }); - + ide.addEventListener("tab.create", function(e) { var page = e.page; var xmlNode = e.doc.getNode(); if (!apf.isTrue(xmlNode.getAttribute("clone"))) return; - + var id = page.id; var pages = tabs.getPages(); var origPage; @@ -201,12 +201,12 @@ module.exports = ext.register("ext/splitview/splitview", { // if 2nd page found, join em! if (!origPage) return; - + //Splits.consolidateEditorSession(origPage, origPage.$editor.amlEditor); page.$doc = origPage.$doc; page.setAttribute("actiontracker", origPage.$at); page.$at = origPage.$at; - + // find the settings node that corresponds with the clone view // that is being constructed right now var settings, pages, indices, idx; @@ -236,20 +236,20 @@ module.exports = ext.register("ext/splitview/splitview", { page.$doc.addEventListener("init", cont); else cont(); - + function cont() { var editor = Splits.getCloneEditor(page); - + page.acesession = new EditSession(page.$doc.acedoc); page.acesession.setUndoManager(Splits.CloneUndoManager); - + page.$doc.addEventListener("prop.value", function(e) { page.acesession.setValue(e.value || ""); editor.$editor.moveCursorTo(0, 0); }); - + editor.setProperty("value", page.acesession); - + var split = Splits.create(origPage); split.clone = true; split.pairs.push({ @@ -293,18 +293,18 @@ module.exports = ext.register("ext/splitview/splitview", { } } }); - + Splits.init(this); }, - + mergetableft: function() { return this.mergeTab("left"); }, - + mergetabright: function() { return this.mergeTab("right"); }, - + mergeTab: function(dir) { var bRight = dir == "right"; var tabs = tabEditors; @@ -316,7 +316,7 @@ module.exports = ext.register("ext/splitview/splitview", { curr = split.pairs[bRight ? splitLen - 1 : 0].page; if (!curr || pages.length == 1) return; - + var idx; if (splitLen == 3) { // if the max amount of tabs has been reached inside a split view, @@ -339,15 +339,15 @@ module.exports = ext.register("ext/splitview/splitview", { this.save(); return false; }, - + nexteditor: function() { this.cycleEditors("next"); }, - + preveditor: function() { this.cycleEditors("prev"); }, - + cycleEditors: function(dir) { var split = Splits.getActive(); if (!split) @@ -364,7 +364,7 @@ module.exports = ext.register("ext/splitview/splitview", { Splits.setActivePage(split, split.pairs[idx].page); return false; }, - + /** * Invoked when a file is closed * @@ -378,11 +378,11 @@ module.exports = ext.register("ext/splitview/splitview", { Splits.update(); this.save(); }, - + /** * Invoked when a tab is clicked, that is the part of the tab-button that is NOT * a close button. - * + * * @param {AmlEvent} e */ onTabClick: function(e) { @@ -402,13 +402,13 @@ module.exports = ext.register("ext/splitview/splitview", { } Splits.setActivePage(split, page); // only the first tab in the split view is the trigger to select all - // other tabs as well (because only the page of the first tab is + // other tabs as well (because only the page of the first tab is // REALLY shown) if (ret !== false && page !== split.pairs[0].page) { tabs.set(split.pairs[0].page); ret = false; } - + if (!shiftKey) return true; @@ -432,12 +432,12 @@ module.exports = ext.register("ext/splitview/splitview", { return ret; } }, - + /** * Tab cycling is handled by the tabbehaviors extension, which emits an event * we can hook into. We correct the tab to switch to if a user lands onto a * split view while cycling. - * + * * @param {AmlEvent} e */ onCycleTab: function(e) { @@ -447,14 +447,14 @@ module.exports = ext.register("ext/splitview/splitview", { return; if (split.pairs.length == pages.length) return (e.returnValue = false); - + var maxIdx = pages.length - 1; var bRight = e.dir == "right"; var idx = pages.indexOf(split.pairs[bRight ? split.pairs.length - 1 : 0].page) + (bRight ? 1 : -1); idx = idx < 0 ? maxIdx : idx > maxIdx ? 0 : idx; if (Splits.indexOf(split, pages[idx]) > -1) return (e.returnValue = false); - + // check if the next tab is inside a split as well: split = Splits.get(pages[idx])[0]; if (split) @@ -462,7 +462,7 @@ module.exports = ext.register("ext/splitview/splitview", { else e.returnValue = idx; }, - + updateSplitView: function(previous, next) { //if (restoring) // return; @@ -471,14 +471,14 @@ module.exports = ext.register("ext/splitview/splitview", { var at = next.$at; // check if this is a valid clone session var split = Splits.get(next)[0]; - + // hide the previous split view if (previous && previous.$model) { var oldSplit = Splits.get(previous)[0]; if (oldSplit && (!split || oldSplit.gridLayout != split.gridLayout)) Splits.hide(oldSplit); } - + // enable split view ONLY for code editors for now... if (this.isSupportedEditor(next.$editor)) { mnuCloneView.enable(); @@ -488,7 +488,7 @@ module.exports = ext.register("ext/splitview/splitview", { mnuCloneView.disable(); mnuSplitAlign.disable(); } - + mnuCloneView.setAttribute("checked", false); mnuSplitAlign.setAttribute("checked", false); @@ -508,41 +508,43 @@ module.exports = ext.register("ext/splitview/splitview", { Splits.show(split); mnuSplitAlign.setAttribute("checked", split.gridLayout == "3rows"); - + if (split.clone) mnuCloneView.setAttribute("checked", true); - + else + mnuCloneView.disable(); + apf.layout.forceResize(); - + this.save(); - + return false; }, - + changeLayout: function(page, gridLayout) { var split = Splits.get(page)[0]; if (!split || split.gridLayout == gridLayout) return; - + Splits.update(split, gridLayout); mnuSplitAlign.setAttribute("checked", gridLayout == "3rows"); this.save(); }, - + /** - * + * */ startCloneView: function(page) { var split = this.getCloneView(page); var doc = page.$doc; - + if (split || !doc || !Splits.getEditorSession(page)) return; apf.xmldb.setAttribute(doc.getNode(), "clone", true); Editors.openEditor(doc, false, false, true); }, - + endCloneView: function(page) { mnuCloneView.setAttribute("checked", false); var split = this.getCloneView(page); @@ -552,7 +554,7 @@ module.exports = ext.register("ext/splitview/splitview", { delete split.clone; apf.xmldb.setAttribute(page.$doc.getNode(), "clone", false); }, - + getCloneView: function(page) { var splits = Splits.get(page); if (!splits.length) @@ -564,7 +566,7 @@ module.exports = ext.register("ext/splitview/splitview", { } return null; }, - + save: function() { if (!Settings.model || restoring) return; @@ -573,7 +575,7 @@ module.exports = ext.register("ext/splitview/splitview", { var i, l; for (i = node.childNodes.length - 1; i >= 0; --i) node.removeChild(node.childNodes[i]); - + var splits = Splits.get(); var splitEl; for (i = 0, l = splits.length; i < l; ++i) { @@ -588,26 +590,26 @@ module.exports = ext.register("ext/splitview/splitview", { } apf.xmldb.applyChanges("synchronize", node); }, - + restore: function(settings) { // no tabs open... don't bother ;) var tabs = tabEditors; if (tabs.getPages().length <= 1) return; - + var nodes; var splits = Splits.get(); if (apf.isArray(settings)) nodes = settings; else nodes = settings.selectNodes("splits/split"); - + if (!nodes || !nodes.length) return; - + restoring = true; var activePage = tabs.getPage(); - + var node, ids, j, l2, id, dupes, hasClone, split, page, editor, active; for (var i = nodes.length - 1; i >= 0; --i) { node = nodes.pop(); @@ -625,12 +627,12 @@ module.exports = ext.register("ext/splitview/splitview", { hasClone = id; } } - + ids = Object.keys(dupes); l2 = ids.length; if (l2 < 2) continue; - + if (hasClone) { page = tabs.getPage(hasClone); if (!page) @@ -673,7 +675,7 @@ module.exports = ext.register("ext/splitview/splitview", { editor: editor }); } - + if (split.pairs.length > 1) { if (apf.isTrue(node.getAttribute("active"))) active = split; @@ -682,7 +684,7 @@ module.exports = ext.register("ext/splitview/splitview", { } } Splits.set(splits); - + if (!active || Splits.indexOf(active, activePage) == -1) { tabs.set(activePage); } @@ -698,7 +700,7 @@ module.exports = ext.register("ext/splitview/splitview", { if (!restoreQueue.length) restoring = false; }, - + isSupportedEditor: function() { var editor; for (var i = 0, l = arguments.length; i < l; ++i) { @@ -708,7 +710,7 @@ module.exports = ext.register("ext/splitview/splitview", { } return true; }, - + enable : function(){ this.nodes.each(function(item){ item.enable(); @@ -729,4 +731,4 @@ module.exports = ext.register("ext/splitview/splitview", { } }); -}); \ No newline at end of file +}); From 289546c177e89e69042d0616a4111e52fa82bc3e Mon Sep 17 00:00:00 2001 From: mikedeboer Date: Fri, 20 Apr 2012 10:59:57 +0200 Subject: [PATCH 67/69] changed apf.document.body to apf.document.documentElement --- client/ext/splitview/grids.js | 6 +++--- client/ext/splitview/splits.js | 10 +++++----- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/client/ext/splitview/grids.js b/client/ext/splitview/grids.js index dbf06af5622..457546ebc9f 100644 --- a/client/ext/splitview/grids.js +++ b/client/ext/splitview/grids.js @@ -203,11 +203,11 @@ function createNodes(struct, splitters, parent) { if (struct.node) return; - parent = parent || apf.document.body; + parent = parent || apf.document.documentElement; (apf.isArray(struct) ? struct : Object.keys(struct)).forEach(function(nodeName) { var options = {}; if ("vbox|hbox".indexOf(nodeName) > -1) { - if (parent === apf.document.body) { + if (parent === apf.document.documentElement) { options.visible = false; options.anchors = "2 0 0 0"; } @@ -227,7 +227,7 @@ function createNodes(struct, splitters, parent) { splitters.push(node); // if we just appended the main node to the document, set it as the grid's // main node - if (parent === apf.document.body) + if (parent === apf.document.documentElement) struct.node = node; // recurse down the structure's tree if (struct[nodeName] && struct[nodeName] !== 1) diff --git a/client/ext/splitview/splits.js b/client/ext/splitview/splits.js index dc9e8b2e3d8..edc7d071321 100644 --- a/client/ext/splitview/splits.js +++ b/client/ext/splitview/splits.js @@ -195,7 +195,7 @@ exports.update = function(split, gridLayout) { for (var clone, i = 0, l = editors.length; i < l; ++i) { clone = editors[i]; clone.hide(); - apf.document.body.appendChild(clone); + apf.document.documentElement.appendChild(clone); } } @@ -263,7 +263,7 @@ exports.mutate = function(split, page, type) { editor.removeAttribute("actiontracker"); //removeEditorListeners(editor); editor.hide(); - apf.document.body.appendChild(editor); + apf.document.documentElement.appendChild(editor); page.$deactivateButton(); clearSplitViewStyles(page); editor.hide(); @@ -427,7 +427,7 @@ function createEditorClones(editor) { EditorClones.cloneEditor = editor.cloneNode(true); EditorClones.cloneEditor.removeAttribute("id"); EditorClones.cloneEditor.setAttribute("visible", "false"); - apf.document.body.appendChild(EditorClones.cloneEditor); + apf.document.documentElement.appendChild(EditorClones.cloneEditor); addEditorListeners.call(this, EditorClones.cloneEditor); @@ -455,7 +455,7 @@ function createEditorClones(editor) { for (var clone, i = 0, l = EditorClones[id].length; i < l; ++i) { clone = EditorClones[id][i]; clone.hide(); - apf.document.body.appendChild(clone); + apf.document.documentElement.appendChild(clone); } return EditorClones[id]; } @@ -470,7 +470,7 @@ function createEditorClones(editor) { editor.removeAttribute("id"); editor.setAttribute("visible", false); EditorClones[id].push(editor); - apf.document.body.appendChild(editor); + apf.document.documentElement.appendChild(editor); addEditorListeners.call(this, editor); if (isCodeEditor) { editor.$editor.commands = previousEditor.$editor.commands; From 9bf8fd2a1794a32fceb79e22448a6a77124e9366 Mon Sep 17 00:00:00 2001 From: mikedeboer Date: Fri, 20 Apr 2012 11:06:46 +0200 Subject: [PATCH 68/69] fixed syntax error --- client/ext/gotoline/gotoline.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/ext/gotoline/gotoline.js b/client/ext/gotoline/gotoline.js index d6d101fa0f1..ce98c8ad35d 100644 --- a/client/ext/gotoline/gotoline.js +++ b/client/ext/gotoline/gotoline.js @@ -225,7 +225,7 @@ module.exports = ext.register("ext/gotoline/gotoline", { ace.gotoLine(line); - if (preview) { + if (typeof preview != "undefined") { var animate = apf.isTrue(settings.model.queryValue("editors/code/@animatedscroll")); if (!animate) return; From 30e82ca4eae4fc08970f84b8331d3139fa3ddde9 Mon Sep 17 00:00:00 2001 From: mikedeboer Date: Fri, 20 Apr 2012 13:14:17 +0200 Subject: [PATCH 69/69] fixed ID clash in searchinfiles plugin --- client/ext/searchinfiles/searchinfiles.js | 7 +++---- client/ext/searchinfiles/searchinfiles.xml | 2 +- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/client/ext/searchinfiles/searchinfiles.js b/client/ext/searchinfiles/searchinfiles.js index f6e8fc9bc4a..7e7204f237c 100644 --- a/client/ext/searchinfiles/searchinfiles.js +++ b/client/ext/searchinfiles/searchinfiles.js @@ -55,11 +55,10 @@ module.exports = ext.register("ext/searchinfiles/searchinfiles", { }, init : function(amlNode){ - this.txtFind = txtSFFind; - this.btnFind = btnSFFind;//winSearchInFiles.selectSingleNode("a:vbox/a:hbox/a:button[3]"); + this.txtFind = txtSFFind; + this.btnFind = btnSFFind;//winSearchInFiles.selectSingleNode("a:vbox/a:hbox/a:button[3]"); this.btnFind.onclick = this.execFind.bind(this, false); - this.txtReplace = txtReplace; this.btnReplaceAll = btnReplaceAll; this.btnReplaceAll.onclick = this.execFind.bind(this, true); @@ -174,7 +173,7 @@ module.exports = ext.register("ext/searchinfiles/searchinfiles", { casesensitive: matchCase, regexp: regex, replaceAll: _self.replaceAll ? "true" : "false", - replacement: txtReplace.value + replacement: txtSFReplace.value }; }, diff --git a/client/ext/searchinfiles/searchinfiles.xml b/client/ext/searchinfiles/searchinfiles.xml index c4d0483e1ec..27e524b4c1c 100644 --- a/client/ext/searchinfiles/searchinfiles.xml +++ b/client/ext/searchinfiles/searchinfiles.xml @@ -20,7 +20,7 @@ Replace With - +