undoRedo.js 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352
  1. /********************************************************************************************************************************
  2. * Licensed Materials - Property of IBM *
  3. * *
  4. * IBM Cognos Products: AGS *
  5. * *
  6. * (C) Copyright IBM Corp. 2005, 2008 *
  7. * *
  8. * US Government Users Restricted Rights - Use, duplication or disclosure restricted by GSA ADP Schedule Contract with IBM Corp. *
  9. *********************************************************************************************************************************/
  10. /*
  11. this file holds the undo redo functionality for text and topics in dropzones
  12. the undo functions maintain stacks of actions and undoneActions.... these are stacked and unstacked as necessary and can be
  13. 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
  14. 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
  15. */
  16. var redoUndoManager = new RedoUndoManager();
  17. function getRedoUndoManager(){
  18. return redoUndoManager;
  19. }
  20. function RedoUndoManager(){
  21. //do we batch reversable actions or not
  22. //if yes batch them and put them all on the undo stack together
  23. //otherwise stack them now
  24. this.editActionBatching;
  25. // undo/redo variables... the array is used as a stack
  26. this.actionHistory = new Stack();
  27. this.undoneHistory = new Stack();
  28. //these have to be separate as they are the only actions that survive the page
  29. this.taskDeleteHistory = new Stack();
  30. }
  31. RedoUndoManager.prototype.startEditActionBatch = function(){
  32. this.editActionBatching = new Array();
  33. }
  34. RedoUndoManager.prototype.isEditActionBatching = function(){
  35. return this.editActionBatching != undefined;
  36. }
  37. RedoUndoManager.prototype.getEditActionBatch = function(){
  38. return this.editActionBatching;
  39. }
  40. RedoUndoManager.prototype.stopEditActionBatch = function (){
  41. this.getActionHistory().push(new EditActionBundle(this.editActionBatching));
  42. this.editActionBatching = undefined;
  43. }
  44. /**
  45. *function to allow external code to put atcionts in the undo / redo stacks
  46. */
  47. RedoUndoManager.prototype.getActionHistory = function(){
  48. return this.actionHistory;
  49. }
  50. /**
  51. *function to allow external code to put atcionts in the undo / redo stacks
  52. */
  53. RedoUndoManager.prototype.getUndoneHistory = function(){
  54. return this.undoneHistory;
  55. }
  56. /**
  57. *a field has changed
  58. */
  59. RedoUndoManager.prototype.updateField = function(fieldName, new_value, old_value){
  60. //ahh a nice tea
  61. var tea = new TextEditAction(new FormObjectHandler(fieldName), new_value, old_value);
  62. var eab = new EditActionBundle(new Array(tea));
  63. if(this.isEditActionBatching()){
  64. this.getEditActionBatch().push(eab);
  65. }else{
  66. this.getActionHistory().push(eab);
  67. }
  68. this.getUndoneHistory().clear();
  69. }
  70. /*
  71. * check for a valid undo step
  72. */
  73. RedoUndoManager.prototype.canUndoTreeAction = function() {
  74. return getActionHistory().peek() != null;
  75. }
  76. /*
  77. * Reset the tree action state
  78. */
  79. RedoUndoManager.prototype.clearAgentItemsTracking = function() {
  80. agentItemsListener = new AgentItemsListener();
  81. this.getActionHistory().clear();
  82. this.getUndoneHistory().clear();
  83. this.taskDeleteHistory.clear();
  84. }
  85. /*
  86. * Reset the tree action state
  87. */
  88. RedoUndoManager.prototype.resetTreeAction = function() {
  89. //alert('calling resetTreeAction');
  90. this.getActionHistory().clear();
  91. this.getUndoneHistory().clear();
  92. }
  93. /*
  94. check whether this task has been deleted by interogating the taskDeleteHistory stack
  95. */
  96. RedoUndoManager.prototype.isDeletedTask = function(taskId){
  97. var foundBundle = null;
  98. foundBundle = this.taskDeleteHistory.search(new taskDeleteStackSearcher(taskId));
  99. return foundBundle && foundBundle.length ? foundBundle.length > 0 : false;
  100. }
  101. function taskDeleteStackSearcher(taskId){
  102. this.searchTaskId = taskId;
  103. this.search = function(editActionBundle){
  104. if(this.searchTaskId == editActionBundle.taskId){
  105. return true;
  106. }else{
  107. return false;
  108. }
  109. }
  110. }
  111. /*
  112. * Switch the last tree action as a result of an undo/redo,
  113. * so that if an item was just added, then we delete it and vice versa
  114. */
  115. RedoUndoManager.prototype.redoLastEditAction = function() {
  116. //alert("redo");
  117. var success=false;
  118. if (this.undoneHistory.peek() != null) {
  119. var undoneAction = this.undoneHistory.pop();
  120. success = this.doEditAction(undoneAction, false);
  121. if(success){
  122. this.actionHistory.push(undoneAction);
  123. }
  124. }
  125. return success;
  126. }
  127. /*
  128. * take an EditAction and carry it out....
  129. * undo is a boolean indicating whether we are undo or redo
  130. */
  131. RedoUndoManager.prototype.doEditAction = function(editAction, undo) {
  132. var success=true;
  133. //do the bundles contents recursively
  134. if(editAction instanceof EditActionBundle){
  135. if(undo){
  136. for(var i = editAction.editActions.length - 1; i >= 0 && success; i--){
  137. success = success && this.doEditAction(editAction.editActions[i], undo);
  138. }
  139. }else{
  140. for(var i = 0; i < editAction.editActions.length && success; i++){
  141. success = success && this.doEditAction(editAction.editActions[i], undo);
  142. }
  143. }
  144. return success;
  145. }
  146. //perform the action
  147. if ((editAction instanceof DeleteItemEditAction && undo) || (editAction instanceof AddItemEditAction && !undo)) {
  148. //alert('adding '+editAction.outerHTML+' to the agent items tree');
  149. //just get the original object and recreate it
  150. success = addUnknownItem(editAction.refObject.getValue(), true);
  151. }else if(editAction instanceof TextEditAction){
  152. var value;
  153. if(undo){
  154. value = editAction.originalText;
  155. }else{
  156. value = editAction.editedText;
  157. }
  158. editAction.editorObject.setFormObjectValue(value);
  159. var domObjectId = editAction.editorObject.form_object_id;
  160. //keep the agent items listener up to date
  161. getAgentItemsListener().redoUndoTopicsUpdate(domObjectId,value);
  162. //keep the focus
  163. if (editAction.editorObject != undefined) {
  164. editAction.editorObject.getFormObject().focus();
  165. }
  166. }else if((editAction instanceof DeleteItemEditAction && !undo) || (editAction instanceof AddItemEditAction && undo)){
  167. //alert('deleting '+editAction.outerHTML+' from the agent items tree');
  168. //undefined result for delete is a success... so leave it
  169. deleteDataItemNode(editAction.refObject,true) ;
  170. }else{
  171. success = false;
  172. }
  173. return success;
  174. }
  175. /*
  176. * Switch the last tree action as a result of an undo/redo,
  177. * so that if an item was just added, then we delete it and vice versa
  178. */
  179. RedoUndoManager.prototype.reverseLastEditAction = function() {
  180. //alert("undoing");
  181. var success=false;
  182. if (this.actionHistory.peek() != null) {
  183. var editAction = this.actionHistory.pop();
  184. //do the undo do
  185. success = this.doEditAction(editAction, true);
  186. if(success){
  187. this.undoneHistory.push(editAction);
  188. }
  189. }else if(this.taskDeleteHistory.peek()){
  190. //we have a task delete to undo... hold your breath
  191. //undo the tree changes
  192. this.doEditAction(this.taskDeleteHistory.pop(), true);
  193. //on your knees morphlet
  194. var msgFrame = getMessageIFrame();
  195. var msgDoc = getFrameDocument(msgFrame);
  196. // only undo task delete
  197. if(msgDoc.pform.m.value.indexOf("dialogAdapter")!= -1) {
  198. //set the dialog morphlet
  199. msgDoc.pform.m.value="/ags/dialogAdapter.xts";
  200. if(msgDoc.pform.m.value){
  201. msgDoc.pform.agentItemOp.value="undo";
  202. }else{
  203. messageForm.appendChild(createHiddenInput(msgDoc, "agentItemOp", "undo"));
  204. }
  205. }
  206. msgDoc.pform.submit();
  207. }
  208. return success;
  209. }
  210. //wraps a group of actions to be rolled back
  211. // or forward at the same time
  212. function EditActionBundle(editActions){
  213. if(editActions && editActions.length && editActions.length > 0){
  214. this.editActions = editActions;
  215. }else{
  216. this.editActions = new Array();
  217. }
  218. }
  219. function TaskEditActionBundle(editActions, taskId){
  220. var eab = new EditActionBundle(editActions);
  221. eab.taskId = taskId;
  222. return eab;
  223. }
  224. EditActionBundle.prototype.addEditAction = function(editAction){
  225. if(editAction){
  226. this.editActions.push(editAction);
  227. }
  228. }
  229. function DeleteItemEditAction(refObject){
  230. this.refObject = refObject;
  231. }
  232. function AddItemEditAction(refObject){
  233. this.refObject = refObject;
  234. }
  235. function TextEditAction(editor_object, edited_text, original_text){
  236. this.editorObject = editor_object;
  237. this.editedText = edited_text;
  238. this.originalText = original_text;
  239. }
  240. //this wraps the form elements used in the undo redo
  241. //as their not all the same
  242. function FormObjectHandler(form_object_id){
  243. this.form_object_id = form_object_id;
  244. this.form_object;
  245. }
  246. FormObjectHandler.prototype.getFormObject = function(){
  247. if(!this.form_object){
  248. this.form_object = agsFormUtils.getElementByIdOrName(this.form_object_id);
  249. }
  250. return this.form_object;
  251. }
  252. FormObjectHandler.prototype.getFormObjectValue = function(){
  253. var cf = getConfigFrame();
  254. var v_sFrame = getFrame(cf.dropFrame);
  255. if(v_sFrame && v_sFrame.droppy ){
  256. return v_sFrame.droppy.getDropZoneValue(this.form_object_id);
  257. } else {
  258. if(this.getFormObject().value != undefined){
  259. return this.getFormObject().value;
  260. }else if(this.getFormObject().innerHTML != undefined){
  261. return this.getFormObject().innerHTML;
  262. }
  263. }
  264. }
  265. FormObjectHandler.prototype.setFormObjectValue = function(new_value){
  266. var cf = getConfigFrame();
  267. var v_sFrame = getFrame(cf.dropFrame);
  268. if(v_sFrame && v_sFrame.droppy ){
  269. v_sFrame.droppy.setElementValue(this.form_object_id, new_value);
  270. }else{
  271. if(this.getFormObject().value != undefined){
  272. this.getFormObject().value = new_value;
  273. }else if(this.getFormObject().innerHTML != undefined){
  274. this.getFormObject().innerHTML = new_value;
  275. }
  276. }
  277. //now perform any custom actions if we need to
  278. if (v_sFrame.postEditAction) {
  279. v_sFrame.postEditAction();
  280. }
  281. }