Просмотр исходного кода

Merge branch 'feature/documenteditor_history' into 'develop'

Merge of feature/documenteditor_history to develop [版式文件]正文痕迹保留和回放功能

See merge request o2oa/o2oa!201
胡起 5 лет назад
Родитель
Сommit
047ad31d8e
20 измененных файлов с 1226 добавлено и 155 удалено
  1. 1 1
      o2web/source/o2_core/o2/o2.core.js
  2. 34 5
      o2web/source/o2_core/o2/o2.more.js
  3. 7 5
      o2web/source/o2_core/o2/xDesktop/WebSocket.js
  4. 6 3
      o2web/source/o2_lib/diff-match-patch/diff_match_patch_uncompressed.js
  5. 69 0
      o2web/source/x_component_process_Xform/$Form/default/doc.wcss
  6. BIN
      o2web/source/x_component_process_Xform/$Form/default/documenteditoricon/exit.png
  7. BIN
      o2web/source/x_component_process_Xform/$Form/default/documenteditoricon/next.png
  8. BIN
      o2web/source/x_component_process_Xform/$Form/default/documenteditoricon/next_gray.png
  9. BIN
      o2web/source/x_component_process_Xform/$Form/default/documenteditoricon/pause.png
  10. BIN
      o2web/source/x_component_process_Xform/$Form/default/documenteditoricon/pause_gray.png
  11. BIN
      o2web/source/x_component_process_Xform/$Form/default/documenteditoricon/play.png
  12. BIN
      o2web/source/x_component_process_Xform/$Form/default/documenteditoricon/play_gray.png
  13. BIN
      o2web/source/x_component_process_Xform/$Form/default/documenteditoricon/prev.png
  14. BIN
      o2web/source/x_component_process_Xform/$Form/default/documenteditoricon/prev_gray.png
  15. BIN
      o2web/source/x_component_process_Xform/$Form/default/documenteditoricon/stop.png
  16. BIN
      o2web/source/x_component_process_Xform/$Form/default/documenteditoricon/stop_gray.png
  17. 130 140
      o2web/source/x_component_process_Xform/Documenteditor.js
  18. 6 0
      o2web/source/x_component_process_Xform/Form.js
  19. 18 1
      o2web/source/x_component_process_Xform/lp/zh-cn.js
  20. 955 0
      o2web/source/x_component_process_Xform/widget/DocumentHistory.js

+ 1 - 1
o2web/source/o2_core/o2/o2.core.js

@@ -311,7 +311,7 @@
                 //         layout.session.token = xToken;
                 //     }
                 // }
-                o2.runCallback(callback, "success", [responseJSON])
+                o2.runCallback(callback, "success", [responseJSON]);
             },
             onFailure: function(xhr){
                 o2.runCallback(callback, "requestFailure", [xhr]);

+ 34 - 5
o2web/source/o2_core/o2/o2.more.js

@@ -219,15 +219,18 @@
     });
     if (window.Element && Element.implement) Element.implement({
         "isIntoView": function() {
-            var pNode = this.getParent();
-            while (pNode && ((pNode.getScrollSize().y-(pNode.getComputedSize().height+1)<=0) || pNode.getStyle("overflow")==="visible")) pNode = pNode.getParent();
+            // var pNode = this.getParent();
+            // while (pNode && ((pNode.getScrollSize().y-(pNode.getComputedSize().height+1)<=0) || pNode.getStyle("overflow")==="visible")) pNode = pNode.getParent();
+            //
+            var pNode = this.getParentSrcollNode();
 
             if (!pNode) pNode = document.body;
             var size = pNode.getSize();
             var srcoll = pNode.getScroll();
             var p = (pNode == window) ? {"x":0, "y": 0} : this.getPosition(pNode);
             var nodeSize = this.getSize();
-            return (p.x-srcoll.x>=0 && p.y-srcoll.y>=0) && (p.x+nodeSize.x<size.x+srcoll.x && p.y+nodeSize.y<size.y+srcoll.y)
+            //return (p.x-srcoll.x>=0 && p.y-srcoll.y>=0) && (p.x+nodeSize.x<size.x+srcoll.x && p.y+nodeSize.y<size.y+srcoll.y);
+            return (p.x-srcoll.x>=0 && p.y>=0) && (p.x+nodeSize.x<size.x+srcoll.x && p.y+nodeSize.y<size.y)
         },
         "appendHTML": function(html, where){
             if (this.insertAdjacentHTML){
@@ -417,13 +420,39 @@
                 "onClick": click
             });
         },
+        "scrollIn": function(where){
+            var wh = (where) ? where.toString().toLowerCase() : "center";
+
+            if (Browser.name=="ie" || Browser.name=="safari"){
+                var scrollNode = this.getParentSrcollNode();
+                var scrollFx = new Fx.Scroll(scrollNode);
+                var scroll = scrollNode.getScroll();
+                var size = scrollNode.getSize();
+                var thisSize = this.getComputedSize();
+                var p = this.getPosition(scrollNode);
+
+                if (wh=="start"){
+                    var top = 0;
+                    scrollFx.start(scroll.x, p.y-top+scroll.y);
+                }else if (wh=="end"){
+                    var bottom = size.y-thisSize.totalHeight;
+                    scrollFx.start(scroll.x, p.y-bottom+scroll.y);
+                }else{
+                    var center = size.y/2-thisSize.totalHeight/2;
+                    scrollFx.start(scroll.x, p.y-center+scroll.y);
+                }
+            }else{
+                if (wh!=="start" && wh!=="end") wh = "center"
+                this.scrollIntoView({"behavior": "smooth", "block": wh, "inline": "nearest"});
+            }
+        },
         scrollToNode: function(el, where){
             var scrollSize = this.getScrollSize();
             if (!scrollSize.y) return true;
             var wh = (where) ? where.toString().toLowerCase() : "bottom";
             var node = $(el);
-            var size = el.getComputedSize();
-            var p = el.getPosition(this);
+            var size = node.getComputedSize();
+            var p = node.getPosition(this);
             var thisSize = this.getComputedSize();
             var scroll = this.getScroll();
             if (wh==="top"){

+ 7 - 5
o2web/source/o2_core/o2/xDesktop/WebSocket.js

@@ -50,11 +50,13 @@ MWF.xDesktop.WebSocket = new Class({
             }catch(e){
                 //WebSocket.close();
                 //this.webSocket = new WebSocket(this.ws);
-                console.log("Unable to connect to the websocket server, will retry in "+(this.heartTimeout/1000)+" seconds");
-                if (this.webSocket){
-                    this.close();
-                    //this.webSocket = new WebSocket(this.ws);
-                }
+                this.errorCount++;
+                console.log("Unable to connect to the websocket server, will retry in "+(this.checkingTimeout/1000)+" seconds");
+                this.checkRetry();
+                // if (this.webSocket){
+                //     this.close();
+                //     //this.webSocket = new WebSocket(this.ws);
+                // }
             }
             this.heartbeat();
         }

+ 6 - 3
o2web/source/o2_lib/diff-match-patch/diff_match_patch_uncompressed.js

@@ -1257,13 +1257,16 @@ diff_match_patch.prototype.diff_prettyHtml = function(diffs) {
   for (var x = 0; x < diffs.length; x++) {
     var op = diffs[x][0];    // Operation (insert, delete, equal)
     var data = diffs[x][1];  // Text of change.
-    var text = data.replace(pattern_amp, '&amp;').replace(pattern_lt, '&lt;')
-        .replace(pattern_gt, '&gt;').replace(pattern_para, '&para;<br>');
+    // var text = data.replace(pattern_amp, '&amp;').replace(pattern_lt, '&lt;')
+    //     .replace(pattern_gt, '&gt;').replace(pattern_para, '&para;<br>');
+    var text = data;
     switch (op) {
       case DIFF_INSERT:
+        //text = data.replace(pattern_para, '&para;<br>');
         html[x] = '<ins style="background:#e6ffe6;">' + text + '</ins>';
         break;
       case DIFF_DELETE:
+        //text = data.replace(pattern_para, '&para;<br>');
         html[x] = '<del style="background:#ffe6e6;">' + text + '</del>';
         break;
       case DIFF_EQUAL:
@@ -1898,7 +1901,7 @@ diff_match_patch.prototype.patch_apply = function(patches, text) {
               text = text.substring(0, start_loc + index2) + mod[1] +
                      text.substring(start_loc + index2);
             } else if (mod[0] === DIFF_DELETE) {  // Deletion
-              text = text.substring(0, start_loc + index2) +
+              text = text.substring(0, start_loc + index2) + mod[1] +
                      text.substring(start_loc + this.diff_xIndex(diffs,
                          index1 + mod[1].length));
             }

+ 69 - 0
o2web/source/x_component_process_Xform/$Form/default/doc.wcss

@@ -138,6 +138,75 @@
         "line-height": "20px",
         "font-size": "12px",
         "overflow": "hidden"
+    },
+    "historyInforNode": {
+        "width": "300px",
+        "padding": "10px",
+        "overflow": "hidden",
+        "border": "1px solid #CCCCCC",
+        "border-radius": "5px",
+        "box-shadow": "2px 2px 5px #CCCCCC",
+        "position": "absolute",
+        "opacity": 1
+    },
+    "historyListAreaNode": {
+        "width": "260px",
+        "float": "right",
+        "background-color": "#f3f3f3",
+        "border-left": "1px solid #cccccc"
+    },
+    "historyListTitleAreaNode":{
+        "height": "80px",
+        "padding": "10px 0px"
+    },
+    "historyListTitleNode": {
+        "font-weight": "bold",
+        "font-size": "14px",
+        "height": "40px",
+        "line-height": "40px",
+        "padding": "0px 10px"
+    },
+    "historyListTitleInsertNode": {
+        "font-size": "12px",
+        "height": "20px",
+        "line-height": "20px",
+        "padding": "0px 10px"
+    },
+    "historyListTitleDeleteNode": {
+        "font-size": "12px",
+        "height": "20px",
+        "line-height": "20px",
+        "padding": "0px 10px"
+    },
+    "historyListContentAreaNode": {
+        "margin": "5px",
+        "border": "1px solid #cccccc",
+        "background-color": "#ffffff",
+        "overflow": "auto"
+    },
+    "historyListItemNode": {
+        "margin-bottom": "10px",
+        "overflow": "hidden",
+        "padding": "5px 10px",
+    },
+
+    "historyListItemPatchNode": {
+        "overflow": "hidden",
+        "margin-bottom": "10px"
+    },
+    "historyListItemDiffsNode": {
+        "overflow": "hidden"
+    },
+    "historyListItemDiffNode": {
+        "overflow": "hidden",
+        "padding": "5px 10px",
+        "cursor": "pointer",
+        "border": "1px solid #ffffff",
+        "border-radius": "5px",
+        "background-color": "#ffffff"
+    },
+    "historyListItemDiffNode_out": {
+        "border": "1px solid #ffffff",
     }
     
 }

BIN
o2web/source/x_component_process_Xform/$Form/default/documenteditoricon/exit.png


BIN
o2web/source/x_component_process_Xform/$Form/default/documenteditoricon/next.png


BIN
o2web/source/x_component_process_Xform/$Form/default/documenteditoricon/next_gray.png


BIN
o2web/source/x_component_process_Xform/$Form/default/documenteditoricon/pause.png


BIN
o2web/source/x_component_process_Xform/$Form/default/documenteditoricon/pause_gray.png


BIN
o2web/source/x_component_process_Xform/$Form/default/documenteditoricon/play.png


BIN
o2web/source/x_component_process_Xform/$Form/default/documenteditoricon/play_gray.png


BIN
o2web/source/x_component_process_Xform/$Form/default/documenteditoricon/prev.png


BIN
o2web/source/x_component_process_Xform/$Form/default/documenteditoricon/prev_gray.png


BIN
o2web/source/x_component_process_Xform/$Form/default/documenteditoricon/stop.png


BIN
o2web/source/x_component_process_Xform/$Form/default/documenteditoricon/stop_gray.png


+ 130 - 140
o2web/source/x_component_process_Xform/Documenteditor.js

@@ -94,7 +94,6 @@ MWF.xApplication.process.Xform.Documenteditor = MWF.APPDocumenteditor =  new Cla
         }
     },
     _createPage: function(callback){
-        debugger;
         var pageContentNode = this._createNewPage().getFirst();
 
         var control = this.getShowControl();
@@ -443,7 +442,6 @@ MWF.xApplication.process.Xform.Documenteditor = MWF.APPDocumenteditor =  new Cla
     },
 
     reSetShow: function(control){
-        debugger;
         if (!control) control = this.getShowControl();
         var m = function(s){ return (control[s]) ? "show" : "hide"; }
 
@@ -481,7 +479,6 @@ MWF.xApplication.process.Xform.Documenteditor = MWF.APPDocumenteditor =  new Cla
         if (this.layout_issuanceDate) this.layout_issuanceDate[m("issuanceDate")]();
         if (this.layout_annotation) this.layout_annotation[m("annotation")]();
 
-        debugger;
         if ((!control.copyto || !this.layout_copytoContent) && (!control.copyto2 || !this.layout_copyto2Content)  && (!control.editionUnit || !this.layout_edition_issuance_unit) && (!control.editionDate || !this.layout_edition_issuance_date)){
             if (this.layout_editionArea) this.layout_editionArea.hide();
         }else{
@@ -509,7 +506,6 @@ MWF.xApplication.process.Xform.Documenteditor = MWF.APPDocumenteditor =  new Cla
                 // });
             }
             if ((!control.editionUnit || !this.layout_edition_issuance_unit) && (!control.editionDate || !this.layout_edition_issuance_date)){
-                debugger
                 if (this.layout_editionArea && (this.layout_edition_issuance_date || this.layout_edition_issuance_unit)){
                     var trs = this.layout_editionArea.getElement("table").rows;
                     trs.item(trs.length-1).destroy();
@@ -638,13 +634,15 @@ MWF.xApplication.process.Xform.Documenteditor = MWF.APPDocumenteditor =  new Cla
                 this._doublePage();
             }
 
-            this.form.addEvent("beforeProcess", function(){
-                this.resetData();
-                if (this.checkSaveNewEdition()) this.saveNewDataEdition();
-                this.notSaveResetData = true;
-            }.bind(this));
+            // this.form.addEvent("beforeProcess", function(){
+            //     this.resetData();
+            //     if (this.checkSaveNewEdition()) this.saveNewDataEdition();
+            //     this.notSaveResetData = true;
+            // }.bind(this));
             this.form.addEvent("beforeSave", function(){
-                if (!this.notSaveResetData) this.resetData();
+                this.resetData();
+                this.checkSaveNewEdition();
+                //if (!this.notSaveResetData) this.resetData();
             }.bind(this));
 
             if (this.json.toWord=="y"){
@@ -652,49 +650,99 @@ MWF.xApplication.process.Xform.Documenteditor = MWF.APPDocumenteditor =  new Cla
                 if (this.json.toWordTrigger=="save")  this.form.addEvent("beforeSave", this.docToWord.bind(this));
                 if (this.json.toWordTrigger=="submit")  this.form.addEvent("beforeProcess", this.docToWord.bind(this));
             }
-
             if (!layout.mobile) this.loadSideToolbar();
 
+            o2.load("/o2_lib/diff-match-patch/diff_match_patch.js");
+
+            var id = this.form.businessData.data["$work"].job;
+            o2.Actions.load("x_processplatform_assemble_surface").DocumentVersionAction.listWithJobCategory(id, this.json.id, function(json){
+                this.historyDocumentList = json.data;
+                if (this.historyDocumentList.length){
+                    o2.Actions.load("x_processplatform_assemble_surface").DocumentVersionAction.get(this.historyDocumentList[this.historyDocumentList.length-1].id, function(json){
+                        var data = JSON.parse(json.data.data);
+                        this.originaHistoryData = data.data;
+                    }.bind(this));
+                }
+            }.bind(this));
+
             if (callback) callback();
         }.bind(this));
-    },
 
-    checkSaveNewEdition: function(){
-        if (!this.allowEdit) return false;
-        var originaData = this.form.businessData.originalData[this.json.id];
-        if (originaData && originaData.filetext != this.data.filetext){
-            return true;
-        }
-        return false;
+        if (!this.form.documenteditorList) this.form.documenteditorList=[];
+        this.form.documenteditorList.push(this);
     },
-    saveNewDataEdition: function(){
+
+    checkSaveNewEdition: function(callback){
         debugger;
+        if (!this.allowEdit || !this.data.filetext || this.data.filetext == this.json.defaultValue.filetext) return false;
         if (this.form.businessData.work){
-            //var data = this.data.filetext;
-
-            var data = "";
-            if (this.editMode && this.filetextEditor){
-                data = this.filetextEditor.container.getText();
+            var originaData = this.form.businessData.originalData[this.json.id];
+            var editionData = {"category": this.json.id};
+            if (!originaData || !originaData.filetext || !this.originaHistoryData){
+                //保存原始版本
+                editionData.data = JSON.stringify({"data": this.data.filetext});
+            }else if (originaData.filetext!=this.data.filetext){
+                //保存历史版本
+                var data = this.data.filetext;
+                var earlyData = originaData.filetext;
+                var dmp = new diff_match_patch();
+                var diff_d = dmp.diff_main(earlyData, data);
+                dmp.diff_cleanupSemantic(diff_d);
+                var patch_list = dmp.patch_make(earlyData, data, diff_d);
+                editionData.data = JSON.stringify({"patchs": dmp.patch_toText(patch_list)});
             }else{
-                if (this.layout_filetext) data = this.layout_filetext.get("text");
+                return false;
+            }
+            o2.Actions.load("x_processplatform_assemble_surface").DocumentVersionAction.create(this.form.businessData.work.id, editionData, function(json){
+                //originaData.filetext = this.data.filetext;
+                if (callback) callback();
+            }.bind(this));
+        }
+    },
+    // saveNewDataEdition: function(callback){
+    //     if (this.form.businessData.work){
+    //         var editionData = {"category": this.json.id};
+    //         if (this.form.businessData.originalData[this.json.id] && this.form.businessData.originalData[this.json.id].filetext){
+    //             var data = this.data.filetext;
+    //             var earlyData = this.form.businessData.originalData[this.json.id].filetext;
+    //             var dmp = new diff_match_patch();
+    //             var diff_d = dmp.diff_main(earlyData, data);
+    //             dmp.diff_cleanupSemantic(diff_d);
+    //             var patch_list = dmp.patch_make(earlyData, data, diff_d);
+    //             editionData.data = {"patchs": dmp.patch_toText(patch_list)};
+    //
+    //         }else{
+    //             editionData.data = {"data": this.data.filetext};
+    //         }
+    //         o2.Actions.load("x_processplatform_assemble_surface").DocumentVersionAction.create(this.form.businessData.work.id, editionData, function(json){
+    //             this.form.businessData.originalData[this.json.id] = this.data.filetext;
+    //             if (callback) callback();
+    //         }.bind(this));
+    //     }
+    // },
+    resizeToolbar: function(){
+        if (this.toolbarNode){
+            var p = this.toolNode.getPosition(this.form.app.content);
+            var size = this.toolNode.getSize();
+            var pl = this.toolbarNode.getStyle("padding-left").toInt();
+            var pr = this.toolbarNode.getStyle("padding-right").toInt();
+            var x = size.x-pl-pr;
+
+            if (p.y<0){
+                this.toolbarNode.inject(this.form.node);
+                this.toolbarNode.setStyles({
+                    "position": "absolute",
+                    "width": ""+x+"px",
+                    "z-index": 20000,
+                    "top": "0px",
+                    "left": ""+p.x+"px"
+                });
+            }else{
+                this.toolbarNode.inject(this.toolNode);
+                this.toolbarNode.setStyles({"position": "static"});
             }
-            // var activity = this.form.businessData.activity;
-            // var job = this.form.businessData.work || this.form.businessData.workCompleted;
-            // var taskUser = (this.form.businessData.task) ? this.form.businessData.task.identity : "";
-            // var taskPerson = (this.form.businessData.task) ? this.form.businessData.task.person : layout.session.user.distinguishedName;
-            // var date = new Date();
-            var editionData = { "data": data };
-
-            o2.Actions.load("x_processplatform_assemble_surface").DocumentVersionAction.create(this.form.businessData.work.id, { "data": data }, function(json){
-
-            });
         }
-
-        //
-        // if (!this.data.editions) this.data.editions = [];
-        // this.data.editions.push(editionData);
     },
-
     resizeSidebar: function(){
         if (this.sidebarNode){
             var fileTextNode = this.contentNode.getElement("div.doc_layout_filetext");
@@ -766,11 +814,10 @@ MWF.xApplication.process.Xform.Documenteditor = MWF.APPDocumenteditor =  new Cla
         });
     },
     _checkScale: function(offset){
-        debugger;
         offset = 0;
         if (this.pages.length){
             var pageSize = this.pages[0].getSize();
-            var contentSize = this.node.getSize();
+            var contentSize = this.contentNode.getSize();
             var contentWidth = (offset) ? contentSize.x-20-offset : contentSize.x-20;
             if (contentWidth<pageSize.x){
                 this.isScale = true;
@@ -778,30 +825,6 @@ MWF.xApplication.process.Xform.Documenteditor = MWF.APPDocumenteditor =  new Cla
                 this.scale = scale;
                 this.zoom();
                 this.resetNodeSize();
-                // var h = this.node.getSize().y;
-                // h = h - contentSize.y*(1-scale);
-                //
-                // this.node.setStyles({
-                //     "height":""+h+"px"
-                // });
-                // this.contentNode.setStyles({
-                //     "transform":"scale(1, "+scale+")",
-                //     "transform-origin": "0px 0px",
-                //     "overflow": "visible"
-                // });
-                //
-                // this._singlePage();
-                // this.pages.each(function(page){
-                //     page.setStyles({
-                //         "transform":"scale("+scale+", 1)",
-                //         "transform-origin": "0px 0px",
-                //         "overflow": "visible",
-                //         "margin-left": "10px"
-                //     });
-                // });
-                //
-                // if (this.doublePageAction) this.doublePageAction.hide();
-
             }
         }
     },
@@ -809,6 +832,7 @@ MWF.xApplication.process.Xform.Documenteditor = MWF.APPDocumenteditor =  new Cla
         if (scale) this.scale = scale;
 
         var w = this.node.getSize().x;
+        if (this.history && this.history.historyListAreaNode) w = w-this.history.historyListAreaNode.getComputedSize().totalWidth-2;
         w = w/this.scale;
         this.contentNode.setStyles({
             "transform":"scale("+this.scale+")",
@@ -816,17 +840,6 @@ MWF.xApplication.process.Xform.Documenteditor = MWF.APPDocumenteditor =  new Cla
             "overflow": "hidden",
             "width": ""+w+"px"
         });
-
-
-        // this._singlePage();
-        // this.pages.each(function(page){
-        //     page.setStyles({
-        //         "transform":"scale("+this.scale+", 1)",
-        //         "transform-origin": "0px 0px",
-        //         "overflow": "visible",
-        //         "margin-left": "10px"
-        //     });
-        // });
     },
 
     _switchReadOrEdit: function(){
@@ -867,60 +880,34 @@ MWF.xApplication.process.Xform.Documenteditor = MWF.APPDocumenteditor =  new Cla
         }.bind(this), "$doc.doc");
     },
     _historyDoc: function(){
-        this.historyAreaNode = new Element("div", {"styles": this.css.historyAreaNode}).inject(this.node);
-        // this.historyAreaActionNode = new Element("div", {"styles": this.css.historyAreaActionNode}).inject(this.historyAreaNode);
-        // this.historyAreaTitleNode = new Element("div", {"styles": this.css.historyAreaTitleNode, "text": MWF.xApplication.process.Xform.LP.documentEditor.historyList}).inject(this.historyAreaNode);
-        // this.historyAreaContentNode = new Element("div", {"styles": this.css.historyAreaContentNode}).inject(this.historyAreaNode);
-        //
-        // var id = this.form.businessData.data["$work"].job;
-        // o2.Actions.load("x_processplatform_assemble_surface").DocumentVersionAction.listWithJob(id, function(json){
-        //     json.data.each(function(d){
-        //         var title = o2.name.cn(d.person) + "(" + d.activityName + ")";
-        //         var time = d.createTime;
-        //         var node = new Element("div", {"styles": this.css.historyItemNode}).inject(this.historyAreaContentNode);
-        //         var titleNode = new Element("div", {"styles": this.css.historyItemTitleNode, "text": title}).inject(node);
-        //         var timeNode = new Element("div", {"styles": this.css.historyItemTimeNode, "text": time}).inject(node);
-        //     }.bind(this));
-        // }.bind(this));
-        //
-        var size = this.node.getSize();
-        this.historyAreaNode.setStyle("height", ""+size.y+"px");
-
-        if (!this.editMode) this._switchReadOrEditInline();
-        if (this.filetextEditor){
-            debugger;
-            //var currentData = this.filetextEditor.getData();
-            var currentData = this.filetextEditor.container.getText();
-            alert(currentData);
-
-            var id = this.form.businessData.data["$work"].job;
-            o2.Actions.load("x_processplatform_assemble_surface").DocumentVersionAction.listWithJob(id, function(json){
-                var d = json.data[0];
-                o2.Actions.load("x_processplatform_assemble_surface").DocumentVersionAction.get(d.id, function(json){
-                    var historyData = json.data.data;
-                    o2.load("/o2_lib/diff-match-patch/diff_match_patch.js", function(){
-                        var dmp = new diff_match_patch();
-                        dmp.Diff_Timeout = parseFloat(10);
-                        dmp.Diff_EditCost = parseFloat(4);
-                        var diff_d = dmp.diff_main(historyData, currentData);
-                        dmp.diff_cleanupSemantic(diff_d);
-                        var diff_ds = dmp.diff_prettyHtml(diff_d);
-
-                        this.historyAreaNode.set("html", diff_ds);
-                    }.bind(this));
-                }.bind(this));
-                //json.data.each(function(d){
-                //     var title = o2.name.cn(d.person) + "(" + d.activityName + ")";
-                //     var time = d.createTime;
-                //     var node = new Element("div", {"styles": this.css.historyItemNode}).inject(this.historyAreaContentNode);
-                //     var titleNode = new Element("div", {"styles": this.css.historyItemTitleNode, "text": title}).inject(node);
-                //     var timeNode = new Element("div", {"styles": this.css.historyItemTimeNode, "text": time}).inject(node);
-                //}.bind(this));
+        this.getHistory(function(){
+            //this.history.play();
+        }.bind(this));
+    },
+    getHistory: function(callback){
+        if (this.history){
+            this.history.active(function(){
+                if (callback) callback();
+            });
+        }else{
+            MWF.xDesktop.requireApp("process.Xform", "widget.DocumentHistory", function(){
+                this.history = new MWF.xApplication.process.Xform.widget.DocumentHistory(this);
+                this.history.load(function(){
+                    if (callback) callback();
+                });
             }.bind(this));
-
         }
+    },
 
+
+    htmlToText: function(html){
+        var tmpdiv = new Element("div", {"html": html});
+        var text = tmpdiv.get("text");
+        tmpdiv.destroy();
+        return text;
     },
+
+
     _readFiletext: function(){
         //this._returnScale();
         this.zoom(1);
@@ -970,7 +957,6 @@ MWF.xApplication.process.Xform.Documenteditor = MWF.APPDocumenteditor =  new Cla
     _createEditor: function(inline){
         if (this.allowEdit){
             this.loadCkeditorFiletext(function(e){
-                debugger;
                 e.editor.focus();
                 e.editor.getSelection().scrollIntoView();
 
@@ -1129,6 +1115,13 @@ MWF.xApplication.process.Xform.Documenteditor = MWF.APPDocumenteditor =  new Cla
             this.toolbar.load();
         }.bind(this));
 
+        this.scrollNode = this.toolbarNode.getParentSrcollNode();
+        if (this.scrollNode){
+            this.scrollNode.addEvent("scroll", function(){
+                this.resizeToolbar();
+            }.bind(this));
+        }
+
         this.doublePageAction = new Element("div", {"styles": this.css.doc_toolbar_doublePage, "text": MWF.xApplication.process.Xform.LP.doublePage}).inject(this.toolbarNode);
         this.doublePageAction.addEvent("click", function(){
             if (this.options.pageShow!=="double"){
@@ -1215,7 +1208,6 @@ MWF.xApplication.process.Xform.Documenteditor = MWF.APPDocumenteditor =  new Cla
         });
     },
     createWaitSplitPage: function(){
-        debugger;
         this.node.mask({
             "style": {
                 "background-color": "#cccccc",
@@ -1300,7 +1292,7 @@ MWF.xApplication.process.Xform.Documenteditor = MWF.APPDocumenteditor =  new Cla
 
         this._createPage(function(control){
             this._loadPageLayout(control);
-
+            
             // this.data = this._getBusinessData();
             // if (!this.data) this.data = this._getDefaultData();
 
@@ -1689,10 +1681,9 @@ MWF.xApplication.process.Xform.Documenteditor = MWF.APPDocumenteditor =  new Cla
                                 var d = Date.parse(n);
                                 return (d.isValid()) ? d.format("%Y年%m月%d日") : n;
                             });
-                            this.data[name] = tmpStrs.join(",")
+                            this.data[name] = tmpStrs.join(",");
                             break;
                         case "mainSend":
-                            debugger;
                             this.data[name] = strs.join(",") + ":";
                             break;
                         default:
@@ -1720,8 +1711,6 @@ MWF.xApplication.process.Xform.Documenteditor = MWF.APPDocumenteditor =  new Cla
                 }
                 break;
             case "script":
-                debugger;
-
                 if (this.json[scriptItem] && this.json[scriptItem].code){
                     var v = this.form.Macro.exec(this.json[scriptItem].code, this);
                     this.data[name] = v;
@@ -1767,7 +1756,7 @@ MWF.xApplication.process.Xform.Documenteditor = MWF.APPDocumenteditor =  new Cla
     reload: function(){
         this.resetData();
     },
-    resetData: function(){
+    resetData: function(diffFiletext){
         if (this.editMode){ this._switchReadOrEditInline(); }
 
         this._computeData(false);
@@ -1780,7 +1769,7 @@ MWF.xApplication.process.Xform.Documenteditor = MWF.APPDocumenteditor =  new Cla
         this._createPage(function(control){
             this._loadPageLayout(control);
 
-            this.setData(this.data);
+            this.setData(this.data, diffFiletext);
             //this._checkSplitPage(this.pages[0]);
 
             this._repage();
@@ -1853,9 +1842,8 @@ MWF.xApplication.process.Xform.Documenteditor = MWF.APPDocumenteditor =  new Cla
             tmpdiv.destroy();
         }
     },
-    setData: function(data){
+    setData: function(data, diffFiletext){
         if (data){
-            debugger;
             this.data = data;
             // this.data["$json"] = this.json;
             this._setBusinessData(data);
@@ -1888,7 +1876,9 @@ MWF.xApplication.process.Xform.Documenteditor = MWF.APPDocumenteditor =  new Cla
             if (this.layout_signer) this.layout_signer.set("text", data.signer || " ");
             if (this.layout_subject) this.layout_subject.set("html", data.subject || " ");
             if (this.layout_mainSend) this.layout_mainSend.set("text", data.mainSend || " ");
-            if (this.layout_filetext){
+            if (diffFiletext) {
+                this.layout_filetext.set("html", diffFiletext);
+            }else if (this.layout_filetext){
                 //this.layout_filetext.set("placeholder", this.json.defaultValue.filetext);
                 this.layout_filetext.set("html", data.filetext || "");
             }

+ 6 - 0
o2web/source/x_component_process_Xform/Form.js

@@ -1352,6 +1352,7 @@ MWF.xApplication.process.Xform.Form = MWF.APPForm = new Class({
 
         return list;
     },
+    //saveDocumentEditor
     submitWork: function (routeName, opinion, medias, callback, processor, data, appendTaskIdentityList, processorOrgList, callbackBeforeSave) {
         if (!this.businessData.control["allowProcessing"]) {
             MWF.xDesktop.notice("error", { x: "right", y: "top" }, "Permission Denied");
@@ -1383,6 +1384,11 @@ MWF.xApplication.process.Xform.Form = MWF.APPForm = new Class({
         }
         this.fireEvent("beforeProcess");
         if (this.app && this.app.fireEvent) this.app.fireEvent("beforeProcess");
+        // if (this.documenteditorList) {
+        //     this.documenteditorList.each(function (module) {
+        //         module.save(history);
+        //     });
+        // }
 
         //处理忽略授权
         var ignoreEmpowerIdentityList = this.getIgnoreImpowerIdentity(processorOrgList);

+ 18 - 1
o2web/source/x_component_process_Xform/lp/zh-cn.js

@@ -161,7 +161,7 @@ MWF.xApplication.process.Xform.LP = {
     "editdoc": "编辑正文",
     "editdocCompleted": "编辑完成",
     "printdoc": "打印正文",
-    "history": "正文痕迹",
+    "history": "正文痕迹审查",
 
     "subformNestedError" : "该表单存在相互嵌套的子表单,请联系管理员!",
     "subpageNestedError" : "该页面存在相互嵌套的子页面,请联系管理员!",
@@ -202,6 +202,23 @@ MWF.xApplication.process.Xform.LP = {
         "editionUnit":"[印发机关]",
         "editionDate":"[印发日期]",
         "historyList": "历史版本文档"
+    },
+    "documentHistory": {
+        "diffContent": "<div>{time}</div>{name} 在 ”{activity}“ 时修改的内容",
+        "insertContent": "<div>{time}</div>{name} 在 ”{activity}“ 时插入的内容:",
+        "deleteContent": "<div>{time}</div>{name} 在 ”{activity}“ 时删除的内容:",
+        "insert": "插入了",
+        "delete": "删除了",
+        "insertTimes": "插入了 {times} 处",
+        "deleteTimes": "删除了 {times} 处",
+        "play": "修改记录回放",
+        "pause": "暂停回放",
+        "stop": "停止修改记录回放",
+        "next": "下一个修改记录",
+        "prev": "上一个修改记录",
+        "exit": "退出痕迹审查",
+        "diff_patch_count": "共有{history}个历史版本,{diff}处修订。",
+        "original": "原始版本"
     }
 
 };

+ 955 - 0
o2web/source/x_component_process_Xform/widget/DocumentHistory.js

@@ -0,0 +1,955 @@
+MWF.xApplication.process.Xform.widget = MWF.xApplication.process.Xform.widget || {};
+MWF.xApplication.process.Xform.widget.DocumentHistory = new Class({
+    Implements: [Options, Events],
+    options: {
+        "speed": 1,
+        "fxTime": 500,
+        "inforTime": 2000
+    },
+    initialize: function(documentEditor, options){
+        this.setOptions(options);
+        this.documentEditor = documentEditor;
+        this.css = this.documentEditor.css;
+    },
+    load: function(callback){
+        this.getHistroyDocumentList(function(){
+            if (this.historyDocumentList && this.historyDocumentList.length){
+                this.getHistoryDataList(function(){
+                    this.createHistoryToolbar();
+                    this.createHistoryListNode();
+
+                    this.documentEditor.options.pageShow = "single";
+                    this.documentEditor.resetData();
+
+                    this.beginDiffHistory();
+
+                    this.loadHistoryToolbar();
+                    this.loadHistoryList();
+                    if (callback) callback();
+                }.bind(this));
+            }
+        }.bind(this));
+    },
+    createHistoryToolbar: function(){
+        this.documentEditor.documentToolbarNode = this.documentEditor.toolbarNode;
+        this.toolbarNode = this.documentEditor.toolbarNode.clone(true);
+        this.toolbarNode.inject(this.documentEditor.toolbarNode, "after");
+        this.documentEditor.toolbarNode = this.toolbarNode;
+        this.documentEditor.documentToolbarNode.hide();
+        this.toolbarNode.empty();
+        if (this.documentEditor.sidebarNode) this.documentEditor.sidebarNode.hide();
+    },
+
+    loadHistoryToolbar: function(){
+        var html = "<span MWFnodetype=\"MWFToolBarButton\" MWFButtonImage=\"/x_component_process_Xform/$Form/default/documenteditoricon/play.png\" title=\""+MWF.xApplication.process.Xform.LP.documentHistory.play+"\" MWFButtonAction=\"play\"></span>";
+        html += "<span MWFnodetype=\"MWFToolBarButton\" MWFButtonImage=\"/x_component_process_Xform/$Form/default/documenteditoricon/pause.png\" title=\""+MWF.xApplication.process.Xform.LP.documentHistory.pause+"\" MWFButtonAction=\"pause\"></span>";
+        html += "<span MWFnodetype=\"MWFToolBarButton\" MWFButtonImage=\"/x_component_process_Xform/$Form/default/documenteditoricon/stop.png\" title=\""+MWF.xApplication.process.Xform.LP.documentHistory.stop+"\" MWFButtonAction=\"stopPlay\"></span>";
+        html += "<span MWFnodetype=\"MWFToolBarSeparator\"></span>";
+        html += "<span MWFnodetype=\"MWFToolBarButton\" MWFButtonImage=\"/x_component_process_Xform/$Form/default/documenteditoricon/prev.png\" title=\""+MWF.xApplication.process.Xform.LP.documentHistory.prev+"\" MWFButtonAction=\"prev\"></span>";
+        html += "<span MWFnodetype=\"MWFToolBarButton\" MWFButtonImage=\"/x_component_process_Xform/$Form/default/documenteditoricon/next.png\" title=\""+MWF.xApplication.process.Xform.LP.documentHistory.next+"\" MWFButtonAction=\"next\"></span>";
+        html += "<span MWFnodetype=\"MWFToolBarSeparator\"></span>";
+        html += "<span MWFnodetype=\"MWFToolBarButton\" MWFButtonImage=\"/x_component_process_Xform/$Form/default/documenteditoricon/exit.png\" title=\""+MWF.xApplication.process.Xform.LP.documentHistory.exit+"\" MWFButtonAction=\"exit\" MWFButtonText=\""+MWF.xApplication.process.Xform.LP.documentHistory.exit+"\"></span>";
+        html += "<span MWFnodetype=\"MWFToolBarSeparator\"></span>";
+
+        var text = MWF.xApplication.process.Xform.LP.documentHistory.diff_patch_count;
+        text = text.replace(/{history}/, this.historyDataList.length).replace(/{diff}/, this.diffCount);
+
+        html += "<span style='float: left; line-height: 24px; color: #666666; margin-left: 10px'>"+text+"</span>";
+
+        this.toolbarNode.set("html", html);
+
+        MWF.require("MWF.widget.Toolbar", function() {
+            this.toolbar = new MWF.widget.Toolbar(this.toolbarNode, {"style": "documentEdit"}, this);
+            this.toolbar.load();
+            this.checkToolbar();
+        }.bind(this));
+    },
+    checkToolbar: function(){
+        if (this.toolbar){
+            if (this.playing){
+                if (this.stop){
+                    this.toolbar.childrenButton[0].enable();
+                    this.toolbar.childrenButton[1].disable();
+
+                    if (this.patchIndex==0 && this.diffIndex==0){
+                        this.toolbar.childrenButton[3].disable();
+                        if (this.diffPatch.length) this.toolbar.childrenButton[4].enable();
+                    }else{
+                        if (this.patchIndex<this.diffPatch.length-1){
+                            this.toolbar.childrenButton[3].enable();
+                            this.toolbar.childrenButton[4].enable();
+                        }else if (this.patchIndex<this.diffPatch.length && this.diffIndex < this.diffPatch[this.patchIndex].patch.diffs.length){
+                            this.toolbar.childrenButton[3].enable();
+                            this.toolbar.childrenButton[4].enable();
+                        }else{
+                            if (this.diffPatch.length) this.toolbar.childrenButton[3].enable();
+                            this.toolbar.childrenButton[4].disable();
+                        }
+                    }
+                }else{
+                    this.toolbar.childrenButton[0].disable();
+                    this.toolbar.childrenButton[1].enable();
+
+                    this.toolbar.childrenButton[3].disable();
+                    this.toolbar.childrenButton[4].disable();
+                }
+                this.toolbar.childrenButton[2].enable();
+
+            }else{
+                this.toolbar.childrenButton[0].enable();
+                this.toolbar.childrenButton[1].disable();
+                this.toolbar.childrenButton[2].disable();
+
+                if (this.patchIndex==0 && this.diffIndex==0){
+                    this.toolbar.childrenButton[3].disable();
+                    if (this.diffPatch.length) this.toolbar.childrenButton[4].enable();
+                }else{
+                    if (this.patchIndex<this.diffPatch.length-1){
+                        this.toolbar.childrenButton[3].enable();
+                        this.toolbar.childrenButton[4].enable();
+                    }else if (this.patchIndex<this.diffPatch.length && this.diffIndex < this.diffPatch[this.patchIndex].patch.diffs.length){
+                        this.toolbar.childrenButton[3].enable();
+                        this.toolbar.childrenButton[4].enable();
+                    }else{
+                        if (this.diffPatch.length) this.toolbar.childrenButton[3].enable();
+                        this.toolbar.childrenButton[4].disable();
+                    }
+                }
+            }
+        }
+    },
+    createHistoryListNode: function(){
+        this.historyListAreaNode = new Element("div", {"styles": this.css.historyListAreaNode}).inject(this.documentEditor.contentNode, "before");
+        this.documentEditor.contentNode.setStyle("width", "auto");
+        this.documentEditor.zoom(1);
+        this.documentEditor._checkScale();
+
+        var size = this.documentEditor.node.getSize();
+        var toolbarSize = this.documentEditor.toolbarNode.getSize();
+        var h = size.y-toolbarSize.y;
+        this.historyListAreaNode.setStyle("height", ""+h+"px");
+
+        this.historyListTitleAreaNode = new Element("div", {"styles": this.css.historyListTitleAreaNode}).inject(this.historyListAreaNode);
+        this.historyListContentAreaNode = new Element("div", {"styles": this.css.historyListContentAreaNode}).inject(this.historyListAreaNode);
+
+        var y = this.historyListContentAreaNode.getEdgeHeight();
+        var title_y = this.historyListTitleAreaNode.getComputedSize().totalHeight;
+        h = h-y-title_y;
+        this.historyListContentAreaNode.setStyle("height", ""+h+"px");
+
+        this.historyListTitleNode = new Element("div", {"styles": this.css.historyListTitleNode}).inject(this.historyListTitleAreaNode);
+        this.historyListTitleInsertNode = new Element("div", {"styles": this.css.historyListTitleInsertNode}).inject(this.historyListTitleAreaNode);
+        this.historyListTitleDeleteNode = new Element("div", {"styles": this.css.historyListTitleDeleteNode}).inject(this.historyListTitleAreaNode);
+
+    },
+    loadHistoryList: function(){
+        var text = MWF.xApplication.process.Xform.LP.documentHistory.diff_patch_count;
+        text = text.replace(/{history}/, this.historyDataList.length).replace(/{diff}/, this.diffCount);
+
+        var insertStr = MWF.xApplication.process.Xform.LP.documentHistory.insertTimes;
+        var deleteStr = MWF.xApplication.process.Xform.LP.documentHistory.deleteTimes;
+        insertStr = insertStr.replace(/{times}/, this.diffInsertCount);
+        deleteStr = deleteStr.replace(/{times}/, this.diffDeleteCount);
+
+        this.historyListTitleNode.set("text", text);
+        this.historyListTitleInsertNode.set("text", insertStr);
+        this.historyListTitleDeleteNode.set("text", deleteStr);
+
+        //var original = this.historyDataList[0];
+        //this.createHistoryListItem(original);
+        this.historyDataList.each(function(historyData){
+            this.createHistoryListItem(historyData);
+        }.bind(this));
+
+        // this.diffPatch.each(function(patchObj){
+        //     this.createHistoryListItem(patchObj);
+        // }.bind(this));
+    },
+    createHistoryListItem: function(historyData){
+        new MWF.xApplication.process.Xform.widget.DocumentHistory.Item(this, historyData);
+    },
+
+
+    getHistoryDataList: function(callback){
+        // if (this.historyDataList && this.historyDataList.length){
+        //     this.getHistoryDataListFinish(this.historyDataList);
+        //     if (callback) callback();
+        // }else{
+            var historyDataList = [];
+            var getDataCount = 0;
+            var idx = 0;
+
+            var checkBeginDiffHistory = function(){
+                if (getDataCount>=this.historyDocumentList.length){
+                    this.getHistoryDataListFinish(historyDataList, callback);
+                }
+            }.bind(this);
+
+            for (var i=this.historyDocumentList.length-1; i>=0; i--){
+                historyDataList.push(null);
+                this.getHistroyDocumentData(this.historyDocumentList[i].id, function(){
+                    getDataCount++;
+                    checkBeginDiffHistory();
+                }.bind(this), idx, historyDataList);
+                idx++;
+            }
+        //}
+    },
+    getHistoryDataListFinish: function(historyDataList, callback){
+        this.historyDataList = historyDataList;
+        this.originaHistoryData = historyDataList[0].json.data || null;
+
+        if (this.documentEditor.allowEdit){
+            o2.load("/o2_lib/diff-match-patch/diff_match_patch_uncompressed.js", function(){
+                var originaData = this.documentEditor.form.businessData.originalData[this.documentEditor.json.id];
+                var data = this.documentEditor.data.filetext;
+                var earlyData = originaData.filetext;
+                if (data!=earlyData){
+                    var dmp = new diff_match_patch();
+                    var diff_d = dmp.diff_main(earlyData, data);
+                    dmp.diff_cleanupSemantic(diff_d);
+                    var patch_list = dmp.patch_make(earlyData, data, diff_d);
+
+                    if (patch_list.length){
+                        var patch = dmp.patch_toText(patch_list);
+                        var patchData = JSON.stringify({"patchs": patch});
+                        var currentData = {
+                            "data": patchData,
+                            "json": {"patchs": patch},
+                            "person": layout.session.user.distinguishedName,
+                            "activityName": this.documentEditor.form.businessData.activity.name,
+                            "createTime" : (new Date()).format("db")
+                        };
+                        this.historyDataList.push(currentData);
+                    }
+                }
+                if (callback) callback();
+            }.bind(this));
+        }else{
+            if (callback) callback();
+        }
+    },
+    getHistroyDocumentData: function(id, callback, i, historyDataList){
+        o2.Actions.load("x_processplatform_assemble_surface").DocumentVersionAction.get(id, function(json){
+            var obj = JSON.parse(json.data.data);
+            json.data.json = obj;
+            if (historyDataList) historyDataList[i] = json.data;
+            if (callback) callback(json.data);
+        }.bind(this));
+    },
+    getHistroyDocumentList: function(callback){
+        //if (!this.historyDocumentList){
+            var id = this.documentEditor.form.businessData.work.job;
+            o2.Actions.load("x_processplatform_assemble_surface").DocumentVersionAction.listWithJobCategory(id, this.documentEditor.json.id, function(json){
+                this.historyDocumentList = json.data;
+                if (callback) callback();
+            }.bind(this));
+        //}else{
+        //   if (callback) callback();
+        //}
+    },
+
+    beginDiffHistory: function(){
+        //o2.load("/o2_lib/diff-match-patch/diff_match_patch_uncompressed.js", function(){
+            this.initAnimation();
+            //if (callback) callback();
+        //}.bind(this));
+    },
+    initAnimation: function(){
+        this.diffPatch =  this.diffHistroy();
+        this.diffCount = 0;
+        this.diffInsertCount = 0;
+        this.diffDeleteCount = 0;
+        this.diffPatch.each(function(patch){
+            patch.patch.diffs.each(function(diff){
+                if (diff[0]!=0) this.diffCount++;
+                if (diff[0]==-1) this.diffDeleteCount++;
+                if (diff[0]==1) this.diffInsertCount++;
+            }.bind(this));
+        }.bind(this));
+
+        // this.initData();
+        this.initAnimationStatus();
+    },
+    initData: function(){
+        this.currentHistoryData = this.originaHistoryData;
+        this.documentEditor.layout_filetext.set("html", this.currentHistoryData);
+        this.patchIndex = 0;
+        this.diffIndex = 0;
+        if (this.originaDiff) this.originaDiff.showCurrent();
+    },
+    initAnimationStatus: function(){
+        this.patchIndex = 0;
+        this.diffIndex = 0;
+        this.currentDiffs = null;
+        this.stop = true;
+        this.step = false;
+        this.playing = false;
+        this.reverse = false;
+        this.options.fxTime = 500;
+        this.options.inforTime = 2000;
+
+        this.checkToolbar();
+    },
+    doAnimationAuto: function(){
+        this.playing = true;
+        this.checkToolbar();
+        this.doPatchAnimation(function(){
+            this.patchIndex = 0;
+            this.diffIndex = 0;
+            this.playing = false;
+            this.stop = true;
+            this.documentEditor.resetData();
+            this.checkToolbar();
+        }.bind(this));
+    },
+    do: function(){
+        if (this.nextPlayPrefixFunction){
+            this.nextPlayPrefixFunction();
+            this.nextPlayPrefixFunction = null;
+        }else{
+            this.doAnimationAuto();
+        }
+    },
+    play: function(){
+        if (!this.playing){
+            this.initData();
+            this.initAnimationStatus();
+        }
+        this.reverse = false;
+        this.stop = false;
+        this.stopWhile = "";
+        this.options.fxTime = 500;
+        this.options.inforTime = 2000;
+
+        this.toolbar.childrenButton[0].disable();
+        this.toolbar.childrenButton[3].disable();
+        this.toolbar.childrenButton[4].disable();
+        this.do();
+    },
+    stopPlay: function(){
+        if (this.playing){
+            this.stop = true;
+            this.playing = false;
+
+            if (this.nextPlayPrefixFunction){
+                this.nextPlayPrefixFunction();
+                this.nextPlayPrefixFunction = null;
+            }
+
+            this.patchIndex = 0;
+            this.diffIndex = 0;
+            this.toolbar.childrenButton[1].disable();
+            this.toolbar.childrenButton[2].disable();
+        }
+    },
+    pause: function(){
+        if (this.playing){
+            this.stop = true;
+            this.toolbar.childrenButton[1].disable();
+            this.toolbar.childrenButton[2].disable();
+        }
+    },
+    next: function(){
+        this.reverse = false;
+        this.options.fxTime = 0;
+        this.options.inforTime = 0;
+        this.stop = true;
+
+        this.toolbar.childrenButton[3].disable();
+        this.toolbar.childrenButton[4].disable();
+
+        if (!this.playing) this.initData();
+        this.do();
+    },
+    prev: function(){
+        this.reverse = true;
+        this.options.fxTime = 0;
+        this.options.inforTime = 0;
+        this.stop = true;
+
+        this.toolbar.childrenButton[3].disable();
+        this.toolbar.childrenButton[4].disable();
+
+        if (!this.playing) this.initData();
+        this.do();
+    },
+    to: function(diff){
+        if (this.nextPlayPrefixFunction){
+            this.playing = false;
+            this.nextPlayPrefixFunction(function(){
+                this.initData();
+                this.initAnimationStatus();
+                this.reverse = false;
+                this.options.fxTime = 0;
+                this.options.inforTime = 0;
+                this.stop = false;
+                this.stopWhile = diff.id;
+                this.toolbar.childrenButton[3].disable();
+                this.toolbar.childrenButton[4].disable();
+
+                this.doAnimationAuto(diff.id);
+
+            }.bind(this));
+            //this.nextPlayPrefixFunction = null;
+        }else{
+            this.initData();
+            this.initAnimationStatus();
+            this.reverse = false;
+            this.options.fxTime = 0;
+            this.options.inforTime = 0;
+            this.stop = false;
+            this.stopWhile = diff.id;
+            this.toolbar.childrenButton[3].disable();
+            this.toolbar.childrenButton[4].disable();
+
+            this.doAnimationAuto(diff.id);
+        }
+    },
+    origina: function(){
+        if (this.nextPlayPrefixFunction){
+            this.playing = false;
+            this.nextPlayPrefixFunction(function(){
+                this.initData();
+                this.initAnimationStatus();
+            }.bind(this));
+            this.nextPlayPrefixFunction = null;
+        }else{
+            this.initData();
+            this.initAnimationStatus();
+        }
+    },
+
+    exit: function(){
+        this.initAnimationStatus();
+        this.options.fxTime = 0;
+        this.options.inforTime = 0;
+        if (this.nextPlayPrefixFunction){
+            this.nextPlayPrefixFunction(function(){
+                this.documentEditor.toolbarNode = this.documentEditor.documentToolbarNode;
+                this.documentEditor.toolbarNode.show();
+                if (this.documentEditor.sidebarNode) this.documentEditor.sidebarNode.show();
+                this.documentEditor.resizeToolbar();
+            }.bind(this));
+            this.nextPlayPrefixFunction = null;
+        }else{
+            this.documentEditor.toolbarNode = this.documentEditor.documentToolbarNode;
+            this.documentEditor.toolbarNode.show();
+            if (this.documentEditor.sidebarNode) this.documentEditor.sidebarNode.show();
+            this.documentEditor.resizeToolbar();
+        }
+
+        this.historyListAreaNode.destroy();
+        this.historyListAreaNode = null;
+        this.documentEditor.zoom(1);
+        this.documentEditor._checkScale();
+
+        this.documentEditor.resetData();
+        this.toolbarNode.hide();
+    },
+    active: function(callback){
+        this.getHistroyDocumentList(function(){
+            if (this.historyDocumentList && this.historyDocumentList.length){
+                this.getHistoryDataList(function(){
+                    this.documentEditor.options.pageShow = "single";
+                    this.documentEditor.resetData();
+
+                    this.beginDiffHistory();
+
+                    this.documentEditor.resetData();
+                    this.toolbarNode.show();
+                    this.documentEditor.documentToolbarNode = this.documentEditor.toolbarNode;
+                    this.documentEditor.documentToolbarNode.hide();
+                    if (this.documentEditor.sidebarNode) this.documentEditor.sidebarNode.hide();
+                    this.documentEditor.toolbarNode = this.toolbarNode;
+                    this.documentEditor.resizeToolbar();
+
+                    var text = MWF.xApplication.process.Xform.LP.documentHistory.diff_patch_count;
+                    text = text.replace(/{history}/, this.historyDataList.length).replace(/{diff}/, this.diffCount);
+                    this.toolbarNode.getLast().set("html", text);
+
+                    this.createHistoryListNode();
+                    this.loadHistoryList();
+
+                    this.documentEditor.options.pageShow = "single";
+                    this.documentEditor.resetData();
+
+                    if (callback) callback();
+
+                }.bind(this));
+            }
+        }.bind(this));
+    },
+
+    diffHistroy: function(){
+        var diffPatch =  [];
+        //var historyPatchs = [];
+        for (var i=1; i<this.historyDataList.length; i++){
+            // var earlyDataText = this.historyDataList[i-1].data;
+            // var laterData = this.historyDataList[i];
+            //
+            // var dmp = new diff_match_patch();
+            // // dmp.Diff_Timeout = parseFloat(10);
+            // // dmp.Diff_EditCost = parseFloat(4);
+            // var diff_d = dmp.diff_main(earlyDataText, laterData.data);
+            // dmp.diff_cleanupSemantic(diff_d);
+            // var patch_list = dmp.patch_make(earlyDataText, laterData.data, diff_d);
+
+            //historyPatchs.push({"patch_list": patch_list, "obj": laterData});
+            var history = this.historyDataList[i];
+            var data = history.json;
+            history.json = data;
+            if (data.patchs){
+                var dmp = new diff_match_patch();
+                var patch_list = dmp.patch_fromText(data.patchs);
+                history.json.patchObj = patch_list;
+
+                patch_list.each(function(patch){
+                    diffPatch.push({"patch":patch, "obj": history});
+                }.bind(this));
+            }
+        }
+        return diffPatch;
+    },
+
+    doPatchAnimation: function(callback){
+        var patchObj = this.diffPatch[this.patchIndex];
+        var patch = patchObj.patch;
+        var obj = patchObj.obj;
+        this.currentDiffs = patch.diffs;
+        this.diffIndex = (this.reverse) ? patch.diffs.length-1 : 0;
+
+        var start = (this.reverse) ? patch.start1+patch.length2 : patch.start1;
+        this.doDiffsAnimation(obj, start, function(){
+            if (this.reverse){
+                this.patchIndex--;
+                if (this.patchIndex>=0){
+                    this.currentHistoryData = this.documentEditor.layout_filetext.get("html");
+                    this.doPatchAnimation(callback);
+                }else{
+                    if (callback) callback();
+                }
+            }else{
+                this.patchIndex++;
+                if (this.patchIndex<this.diffPatch.length){
+                    this.currentHistoryData = this.documentEditor.layout_filetext.get("html");
+                    this.doPatchAnimation(callback);
+                }else{
+                    if (callback) callback();
+                }
+            }
+
+        }.bind(this));
+    },
+    doPatchAnimationStep: function(i){
+        if (this.patchIndex>this.diffPatch.length || this.patchIndex<0){
+            this.initAnimationStatus();
+            this.documentEditor.resetData();
+            this.checkToolbar();
+        }else{
+            var patchObj = this.diffPatch[this.patchIndex];
+            var patch = patchObj.patch;
+            var obj = patchObj.obj;
+            this.currentDiffs = patch.diffs;
+            this.diffIndex = 0;
+
+            this.doDiffsAnimation(obj, patch.start1);
+
+            this.patchIndex = this.patchIndex+i;
+        }
+    },
+
+    createDiifInforNode: function(obj, node, color, insertInfor){
+        if (this.historyInforDiv){
+            this.historyInforDiv.dispose();
+            this.historyInforDiv = null;
+        }
+        var insertInforDiv = new Element("div", { "styles": this.css.historyInforNode }).inject(this.documentEditor.node);
+        insertInforDiv.setStyle("background", color);
+        insertInfor = insertInfor.replace(/{name}/, o2.name.cn(obj.person))
+            .replace(/{activity}/, obj.activityName)
+            .replace(/{time}/, obj.createTime);
+        insertInforDiv.set("html", insertInfor);
+        insertInforDiv.position({
+            "relativeTo": node,
+            "position": 'upperCenter',
+            "edge": 'bottomCenter',
+            "offset": {
+                "x": 0, "y": -10
+            }
+        });
+        this.historyInforDiv = insertInforDiv;
+        return insertInforDiv;
+    },
+    doDiffsAnimation: function(obj, start, callback){
+        var diff = this.currentDiffs[this.diffIndex];
+        var filetextNode = this.documentEditor.layout_filetext;
+        switch (diff[0]) {
+            case DIFF_INSERT:
+                if (diff["item"]) diff["item"].showCurrent((!this.stopWhile || this.stopWhile == diff["id"]));
+                if (this.originaDiff) this.originaDiff.hideCurrent();
+                var text = diff[1];
+                if (this.reverse){
+                    start -= text.length;
+                    var left = this.currentHistoryData.substring(0, start);
+                    var middle = this.currentHistoryData.substring(start, start+diff[1].length);
+                    var right = this.currentHistoryData.substring(start+diff[1].length);
+                    filetextNode.set("html", left+"<ins style='color:blue;'></ins>"+right);
+                }else{
+                    var left = this.currentHistoryData.substring(0, start);
+                    var right = this.currentHistoryData.substring(start);
+                    filetextNode.set("html", left+"<ins style='color:blue;'></ins>"+right);
+                }
+
+                var ins = filetextNode.getElement("ins");
+                if (!this.stopWhile || this.stopWhile == diff["id"]) ins.scrollIn();
+
+                this.doInsetAnimation(ins, diff[1], function(invisible){
+                    var insertInforDiv = null;
+                    if (!invisible && (!this.stopWhile || this.stopWhile == diff["id"]) ){
+                        insertInforDiv = this.createDiifInforNode(obj, ins, "#e2edfb", MWF.xApplication.process.Xform.LP.documentHistory.insertContent);
+                    }
+                    window.setTimeout(function(){
+                        var endFunction = function(cb){
+                            if (insertInforDiv) insertInforDiv.fade("out");
+                            var fx = new Fx.Tween(ins, {property: 'opacity', duration:this.options.speed*this.options.fxTime});
+                            fx.start(1.1).chain(function(){
+                                if (insertInforDiv) insertInforDiv.destroy();
+                                if (diff["item"]) diff["item"].hideCurrent();
+                                if (this.reverse){
+                                    ins.destroy();
+                                    this.currentHistoryData = filetextNode.get("html");
+                                }else{
+                                    data = filetextNode.get("html");
+                                    this.currentHistoryData = data.replace(/<ins[\s\S]*\/ins>/m, text);
+                                    filetextNode.set("html", this.currentHistoryData);
+                                }
+                                if (this.playing){
+                                    if (this.reverse){
+                                        this.diffIndex--;
+                                        if (this.diffIndex>=0){
+                                            window.setTimeout(function(){this.doDiffsAnimation(obj, start, function(){
+                                                if (callback) callback();
+                                                if (cb) cb();
+                                            });}.bind(this), this.options.speed*this.options.fxTime);
+                                            //this.doDiffsAnimation(obj, start, callback);
+                                        }else{
+                                            if (callback) callback();
+                                        }
+                                    }else{
+                                        start += text.length;
+                                        this.diffIndex++;
+                                        if (this.diffIndex<this.currentDiffs.length){
+                                            window.setTimeout(function(){this.doDiffsAnimation(obj, start, function(){
+                                                if (callback) callback();
+                                                if (cb) cb();
+                                            });}.bind(this), this.options.speed*this.options.fxTime);
+                                            //this.doDiffsAnimation(obj, start, callback);
+                                        }else{
+                                            if (callback) callback();
+                                        }
+                                    }
+                                }else{
+                                    this.initAnimationStatus();
+                                    this.documentEditor.resetData();
+                                    if (cb) cb();
+                                }
+
+
+                            }.bind(this));
+                            if (this.nextPlayPrefixFunction) this.nextPlayPrefixFunction = null;
+                        }.bind(this)
+
+                        if (this.stopWhile) if (this.stopWhile == diff["id"]) this.stop = true;
+
+                        if (!this.stop || !this.playing) {
+                            endFunction();
+                        } else{
+                            if (this.stopWhile){
+                                this.stopWhile = "";
+                                //this.playing = false;
+                            }
+                            this.nextPlayPrefixFunction = endFunction;
+
+                        }
+                        this.checkToolbar();
+                    }.bind(this), (invisible ? 100: this.options.speed*this.options.inforTime));
+                }.bind(this));
+                break;
+            case DIFF_DELETE:
+                if (diff["item"]) diff["item"].showCurrent((!this.stopWhile || this.stopWhile == diff["id"]));
+                if (this.originaDiff) this.originaDiff.hideCurrent();
+                var text = diff[1];
+                if (this.reverse){
+                    var left = this.currentHistoryData.substring(0, start);
+                    //var middle = this.currentHistoryData.substring(start, start+diff[1].length);
+                    var right = this.currentHistoryData.substring(start);
+                    filetextNode.set("html", left+"<del style='color: red'>"+text+"</del>"+right);
+                    start -= text.length;
+                }else{
+                    var left = this.currentHistoryData.substring(0, start);
+                    var middle = this.currentHistoryData.substring(start, start+diff[1].length);
+                    var right = this.currentHistoryData.substring(start+diff[1].length);
+                    //start -= .length;
+                    filetextNode.set("html", left+"<del style='color: red'>"+middle+"</del>"+right);
+                }
+
+
+                var del = filetextNode.getElement("del");
+                if (!this.stopWhile || this.stopWhile == diff["id"]) del.scrollIn();
+
+                this.doDeleteAnimation(del, diff, obj, function(deleteInforDiv){
+                    // var deleteInforDiv = null;
+                    // if (!invisible){
+                    //     deleteInforDiv = this.createDiifInforNode(obj, del, "#fbe0e7", MWF.xApplication.process.Xform.LP.documentHistory.deleteContent);
+                    // }
+                    var invisible = !deleteInforDiv;
+                    window.setTimeout(function(){
+                        var endFunction = function(cb){
+                            if (deleteInforDiv) deleteInforDiv.fade("out");
+                            var fx = new Fx.Tween(del, {property: 'opacity', duration:this.options.speed*this.options.fxTime});
+                            fx.start(0.5,0).chain(function(){
+                                if (deleteInforDiv) deleteInforDiv.destroy();
+                                if (diff["item"]) diff["item"].hideCurrent();
+                                if (this.reverse){
+                                    data = filetextNode.get("html");
+                                    this.currentHistoryData = data.replace(/<del[\s\S]*\/del>/m, text);
+                                    filetextNode.set("html", this.currentHistoryData);
+                                }else{
+                                    del.destroy();
+                                    this.currentHistoryData = filetextNode.get("html");
+                                }
+
+                                if (this.playing){
+                                    if (this.reverse){
+                                        this.diffIndex--;
+                                        if (this.diffIndex>=0){
+                                            window.setTimeout(function(){this.doDiffsAnimation(obj, start, function(){
+                                                if (callback) callback();
+                                                if (cb) cb();
+                                            });}.bind(this), this.options.speed*this.options.fxTime);
+                                        }else{
+                                            if (callback) callback();
+                                        }
+                                    }else{
+                                        this.diffIndex++;
+                                        if (this.diffIndex<this.currentDiffs.length){
+                                            window.setTimeout(function(){this.doDiffsAnimation(obj, start, function(){
+                                                if (callback) callback();
+                                                if (cb) cb();
+                                            });}.bind(this), this.options.speed*this.options.fxTime);
+                                        }else{
+                                            if (callback) callback();
+                                        }
+                                    }
+
+                                }else{
+                                    this.initAnimationStatus();
+                                    this.documentEditor.resetData();
+                                    if (cb) cb();
+                                }
+
+                            }.bind(this));
+                            if (this.nextPlayPrefixFunction) this.nextPlayPrefixFunction = null;
+                        }.bind(this)
+
+                        if (this.stopWhile){
+                            if (this.stopWhile == diff["id"]) this.stop = true;
+                        }
+
+                        if (!this.stop || !this.playing) {
+                            endFunction();
+                        } else{
+                            if (this.stopWhile){
+                                this.stopWhile = "";
+                                //this.playing = false;
+                            }
+                            this.nextPlayPrefixFunction = endFunction;
+                        }
+                        this.checkToolbar();
+                    }.bind(this), (invisible ? 100: this.options.speed*this.options.inforTime));
+                }.bind(this));
+                break;
+            case DIFF_EQUAL:
+                if (this.reverse){
+                    start -= diff[1].length;
+                    this.diffIndex--;
+                    if (this.diffIndex>=0){
+                        this.doDiffsAnimation(obj, start, callback);
+                    }else{
+                        if (callback) callback();
+                    }
+                }else{
+                    start += diff[1].length;
+                    this.diffIndex++;
+                    if (this.diffIndex<this.currentDiffs.length){
+                        this.doDiffsAnimation(obj, start, callback);
+                    }else{
+                        if (callback) callback();
+                    }
+                }
+
+                break;
+        }
+    },
+
+    doInsetAnimation: function(node, str, callback){
+        var tmp = new Element("div", {"html": str});
+        if (!tmp.get("text").trim()){
+            if (callback) callback(true);
+        }else{
+            var nodes = tmp.childNodes;
+            this.doInsetNodeAnimation(node, nodes, 0, callback);
+        }
+    },
+    doInsetNodeAnimation: function(ins, nodes, idx, callback){
+        var node = nodes[idx];
+        if (node.nodeType == Node.TEXT_NODE){
+            this.doCharAnimation(ins, node.nodeValue, 0, function(){
+                idx++;
+                if (idx<nodes.length){
+                    this.doInsetNodeAnimation(ins, nodes, idx, callback);
+                }else{
+                    if (callback) callback();
+                }
+            }.bind(this));
+        }else{
+            var duration = this.options.speed*this.options.fxTime/nodes.length
+            var span = new Element("span", {"styles": {"opacity": 0}}).inject(ins);
+            span.appendChild(node);
+            var fx = new Fx.Tween(span, {property: 'opacity', duration:duration});
+            fx.start(0,1).chain(function(){
+                idx++;
+                if (idx<nodes.length){
+                    this.doInsetNodeAnimation(ins, nodes, idx, callback);
+                }else{
+                    if (callback) callback();
+                }
+            }.bind(this));
+        }
+    },
+
+    doCharAnimation: function(node, str, idx, callback){
+        var duration = this.options.speed*this.options.fxTime/str.length;
+        var char = str.charAt(idx);
+        var span = new Element("span", {"styles": {"opacity": 0}, "html": char}).inject(node);
+        var fx = new Fx.Tween(span, {property: 'opacity', duration:duration});
+        fx.start(0,1).chain(function(){
+            idx++;
+            if (idx<str.length){
+                this.doCharAnimation(node, str, idx, callback);
+            }else{
+                if (callback) callback();
+            }
+        }.bind(this));
+    },
+
+    doDeleteAnimation: function(node, diff, obj, callback){
+        var str = diff[1];
+        var tmp = new Element("div", {"html": str});
+        if (!tmp.get("text").trim()){
+            if (callback) callback(null);
+        }else{
+            var deleteInforDiv = (!this.stopWhile || this.stopWhile == diff["id"]) ? this.createDiifInforNode(obj, node, "#fbe0e7", MWF.xApplication.process.Xform.LP.documentHistory.deleteContent) : null;
+            var fx = new Fx.Tween(node, {property: 'opacity', duration:this.options.speed*this.options.fxTime});
+            fx.start(1,0.5).chain(function(){
+                if (callback) callback(deleteInforDiv);
+            }.bind(this));
+        }
+    }
+});
+
+MWF.xApplication.process.Xform.widget.DocumentHistory.Item = new Class({
+    initialize: function(history, historyData){
+        this.history = history;
+        this.documentEditor = this.history.documentEditor;
+        this.css = this.history.css;
+        this.historyData = historyData;
+        this.load();
+    },
+    load: function(){
+        var patchs = this.historyData.json.patchObj || null;
+        var obj = this.historyData;
+
+        this.node = new Element("div", {"styles": this.css.historyListItemNode}).inject(this.history.historyListContentAreaNode);
+        var patchHtml = "<div style='font-weight: bold; height: 30px; line-height: 30px'>"+o2.name.cn(obj.person)+" ["+obj.activityName+"]</div><div style='height: 20px; line-height: 20px; color:#666666'>"+obj.createTime+"</div>"
+        this.patchNode = new Element("div", {"styles": this.css.historyListItemPatchNode, "html": patchHtml}).inject(this.node);
+        this.diffsNode = new Element("div", {"styles": this.css.historyListItemDiffsNode}).inject(this.node);
+
+        var _self = this;
+        if (patchs){
+            patchs.each(function(patch){
+                patch.diffs.each(function(diff){
+                    if (diff[0]!=0){
+                        diff["id"] = (new o2.widget.UUID()).toString();
+                        var tmp = new Element("div", {"html": diff[1]});
+                        infor = tmp.get("text");
+                        var infor = ((infor.length>50) ? infor.substring(0, 50)+"..." : infor);
+
+                        tmp.destroy();
+                        if (diff[0]==-1){
+                            infor = MWF.xApplication.process.Xform.LP.documentHistory.delete +": "+"<span style='color:red'><del>"+infor+"</del></span>"
+                        }else{
+                            infor = MWF.xApplication.process.Xform.LP.documentHistory.insert +": "+"<span style='color:blue'><ins>"+infor+"</ins></span>"
+                        }
+                        diffNode = new Element("div", {"styles": this.css.historyListItemDiffNode, "html": infor}).inject(this.diffsNode);
+                        diffNode.store("diff", diff);
+                        diff["item"] = {
+                            "node": diffNode,
+                            "showCurrent": function(show){
+                                var thisDiff = this.node.retrieve("diff");
+                                var color = (thisDiff[0]==-1) ? "#fbe0e7": "#e2edfb";
+                                this.node.setStyles({"background-color": color});
+
+                                var ss = _self.history.historyListContentAreaNode.getScrollSize();
+                                var s = _self.history.historyListContentAreaNode.getSize();
+                                if (ss.y>s.y) if (show) this.node.scrollIn();
+                            },
+                            "hideCurrent": function(){
+                                this.node.setStyles(_self.css.historyListItemDiffNode);
+                            }
+                        };
+
+                        diffNode.addEvents({
+                            // "mouseover": function(){
+                            //     if (_self.history.stop){
+                            //         var diff = this.retrieve("diff");
+                            //         var color = (diff[0]==-1) ? "red": "blue";
+                            //         this.setStyles({"border-color": color});
+                            //     }
+                            // },
+                            // "mouseout": function(){ if (_self.history.stop) this.setStyles(_self.css.historyListItemDiffNode_out) },
+                            "click": function(){
+                                if (_self.history.stop){
+                                    var diff = this.retrieve("diff");
+                                    _self.history.to(diff);
+                                }
+                            }
+                        });
+
+                    }
+                }.bind(this));
+            }.bind(this));
+        }else{
+            infor = MWF.xApplication.process.Xform.LP.documentHistory.original;
+            diffNode = new Element("div", {"styles": this.css.historyListItemDiffNode, "html": infor}).inject(this.diffsNode);
+            this.history.originaDiff = {
+                "node": diffNode,
+                "showCurrent": function(){
+                    this.node.setStyles({"background-color": "#e2edfb"});
+                    //if (show) this.node.scrollIn();
+                },
+                "hideCurrent": function(){
+                    this.node.setStyles(_self.css.historyListItemDiffNode);
+                }
+            };
+            diffNode.addEvents({
+                "click": function(){
+                    if (_self.history.stop){
+                        _self.history.origina();
+                    }
+                }
+            });
+        }
+    }
+})