/******************************************************************************************************************************** * Licensed Materials - Property of IBM * * * * IBM Cognos Products: AGS * * * * (C) Copyright IBM Corp. 2005, 2008 * * * * US Government Users Restricted Rights - Use, duplication or disclosure restricted by GSA ADP Schedule Contract with IBM Corp. * *********************************************************************************************************************************/ /* this file holds the undo redo functionality for text and topics in dropzones the undo functions maintain stacks of actions and undoneActions.... these are stacked and unstacked as necessary and can be agent items delete / add or text add / delete. if an agent item add is undone it is removed from the tree and added to the undone stack from where it can be redone. for text changes (triggered by the blur or delete events)the text versions are stacked up in the agent items listener structure FieldContents */ var redoUndoManager = new RedoUndoManager(); function getRedoUndoManager(){ return redoUndoManager; } function RedoUndoManager(){ //do we batch reversable actions or not //if yes batch them and put them all on the undo stack together //otherwise stack them now this.editActionBatching; // undo/redo variables... the array is used as a stack this.actionHistory = new Stack(); this.undoneHistory = new Stack(); //these have to be separate as they are the only actions that survive the page this.taskDeleteHistory = new Stack(); } RedoUndoManager.prototype.startEditActionBatch = function(){ this.editActionBatching = new Array(); } RedoUndoManager.prototype.isEditActionBatching = function(){ return this.editActionBatching != undefined; } RedoUndoManager.prototype.getEditActionBatch = function(){ return this.editActionBatching; } RedoUndoManager.prototype.stopEditActionBatch = function (){ this.getActionHistory().push(new EditActionBundle(this.editActionBatching)); this.editActionBatching = undefined; } /** *function to allow external code to put atcionts in the undo / redo stacks */ RedoUndoManager.prototype.getActionHistory = function(){ return this.actionHistory; } /** *function to allow external code to put atcionts in the undo / redo stacks */ RedoUndoManager.prototype.getUndoneHistory = function(){ return this.undoneHistory; } /** *a field has changed */ RedoUndoManager.prototype.updateField = function(fieldName, new_value, old_value){ //ahh a nice tea var tea = new TextEditAction(new FormObjectHandler(fieldName), new_value, old_value); var eab = new EditActionBundle(new Array(tea)); if(this.isEditActionBatching()){ this.getEditActionBatch().push(eab); }else{ this.getActionHistory().push(eab); } this.getUndoneHistory().clear(); } /* * check for a valid undo step */ RedoUndoManager.prototype.canUndoTreeAction = function() { return getActionHistory().peek() != null; } /* * Reset the tree action state */ RedoUndoManager.prototype.clearAgentItemsTracking = function() { agentItemsListener = new AgentItemsListener(); this.getActionHistory().clear(); this.getUndoneHistory().clear(); this.taskDeleteHistory.clear(); } /* * Reset the tree action state */ RedoUndoManager.prototype.resetTreeAction = function() { //alert('calling resetTreeAction'); this.getActionHistory().clear(); this.getUndoneHistory().clear(); } /* check whether this task has been deleted by interogating the taskDeleteHistory stack */ RedoUndoManager.prototype.isDeletedTask = function(taskId){ var foundBundle = null; foundBundle = this.taskDeleteHistory.search(new taskDeleteStackSearcher(taskId)); return foundBundle && foundBundle.length ? foundBundle.length > 0 : false; } function taskDeleteStackSearcher(taskId){ this.searchTaskId = taskId; this.search = function(editActionBundle){ if(this.searchTaskId == editActionBundle.taskId){ return true; }else{ return false; } } } /* * Switch the last tree action as a result of an undo/redo, * so that if an item was just added, then we delete it and vice versa */ RedoUndoManager.prototype.redoLastEditAction = function() { //alert("redo"); var success=false; if (this.undoneHistory.peek() != null) { var undoneAction = this.undoneHistory.pop(); success = this.doEditAction(undoneAction, false); if(success){ this.actionHistory.push(undoneAction); } } return success; } /* * take an EditAction and carry it out.... * undo is a boolean indicating whether we are undo or redo */ RedoUndoManager.prototype.doEditAction = function(editAction, undo) { var success=true; //do the bundles contents recursively if(editAction instanceof EditActionBundle){ if(undo){ for(var i = editAction.editActions.length - 1; i >= 0 && success; i--){ success = success && this.doEditAction(editAction.editActions[i], undo); } }else{ for(var i = 0; i < editAction.editActions.length && success; i++){ success = success && this.doEditAction(editAction.editActions[i], undo); } } return success; } //perform the action if ((editAction instanceof DeleteItemEditAction && undo) || (editAction instanceof AddItemEditAction && !undo)) { //alert('adding '+editAction.outerHTML+' to the agent items tree'); //just get the original object and recreate it success = addUnknownItem(editAction.refObject.getValue(), true); }else if(editAction instanceof TextEditAction){ var value; if(undo){ value = editAction.originalText; }else{ value = editAction.editedText; } editAction.editorObject.setFormObjectValue(value); var domObjectId = editAction.editorObject.form_object_id; //keep the agent items listener up to date getAgentItemsListener().redoUndoTopicsUpdate(domObjectId,value); //keep the focus if (editAction.editorObject != undefined) { editAction.editorObject.getFormObject().focus(); } }else if((editAction instanceof DeleteItemEditAction && !undo) || (editAction instanceof AddItemEditAction && undo)){ //alert('deleting '+editAction.outerHTML+' from the agent items tree'); //undefined result for delete is a success... so leave it deleteDataItemNode(editAction.refObject,true) ; }else{ success = false; } return success; } /* * Switch the last tree action as a result of an undo/redo, * so that if an item was just added, then we delete it and vice versa */ RedoUndoManager.prototype.reverseLastEditAction = function() { //alert("undoing"); var success=false; if (this.actionHistory.peek() != null) { var editAction = this.actionHistory.pop(); //do the undo do success = this.doEditAction(editAction, true); if(success){ this.undoneHistory.push(editAction); } }else if(this.taskDeleteHistory.peek()){ //we have a task delete to undo... hold your breath //undo the tree changes this.doEditAction(this.taskDeleteHistory.pop(), true); //on your knees morphlet var msgFrame = getMessageIFrame(); var msgDoc = getFrameDocument(msgFrame); // only undo task delete if(msgDoc.pform.m.value.indexOf("dialogAdapter")!= -1) { //set the dialog morphlet msgDoc.pform.m.value="/ags/dialogAdapter.xts"; if(msgDoc.pform.m.value){ msgDoc.pform.agentItemOp.value="undo"; }else{ messageForm.appendChild(createHiddenInput(msgDoc, "agentItemOp", "undo")); } } msgDoc.pform.submit(); } return success; } //wraps a group of actions to be rolled back // or forward at the same time function EditActionBundle(editActions){ if(editActions && editActions.length && editActions.length > 0){ this.editActions = editActions; }else{ this.editActions = new Array(); } } function TaskEditActionBundle(editActions, taskId){ var eab = new EditActionBundle(editActions); eab.taskId = taskId; return eab; } EditActionBundle.prototype.addEditAction = function(editAction){ if(editAction){ this.editActions.push(editAction); } } function DeleteItemEditAction(refObject){ this.refObject = refObject; } function AddItemEditAction(refObject){ this.refObject = refObject; } function TextEditAction(editor_object, edited_text, original_text){ this.editorObject = editor_object; this.editedText = edited_text; this.originalText = original_text; } //this wraps the form elements used in the undo redo //as their not all the same function FormObjectHandler(form_object_id){ this.form_object_id = form_object_id; this.form_object; } FormObjectHandler.prototype.getFormObject = function(){ if(!this.form_object){ this.form_object = agsFormUtils.getElementByIdOrName(this.form_object_id); } return this.form_object; } FormObjectHandler.prototype.getFormObjectValue = function(){ var cf = getConfigFrame(); var v_sFrame = getFrame(cf.dropFrame); if(v_sFrame && v_sFrame.droppy ){ return v_sFrame.droppy.getDropZoneValue(this.form_object_id); } else { if(this.getFormObject().value != undefined){ return this.getFormObject().value; }else if(this.getFormObject().innerHTML != undefined){ return this.getFormObject().innerHTML; } } } FormObjectHandler.prototype.setFormObjectValue = function(new_value){ var cf = getConfigFrame(); var v_sFrame = getFrame(cf.dropFrame); if(v_sFrame && v_sFrame.droppy ){ v_sFrame.droppy.setElementValue(this.form_object_id, new_value); }else{ if(this.getFormObject().value != undefined){ this.getFormObject().value = new_value; }else if(this.getFormObject().innerHTML != undefined){ this.getFormObject().innerHTML = new_value; } } //now perform any custom actions if we need to if (v_sFrame.postEditAction) { v_sFrame.postEditAction(); } }