~davewalker/etherpad/ubuntu-unlimited-max-users-and-revisions

« back to all changes in this revision

Viewing changes to infrastructure/ace/www/undomodule.js

  • Committer: James Page
  • Date: 2011-04-13 08:00:43 UTC
  • Revision ID: james.page@canonical.com-20110413080043-eee2nq7y1v7cv2mp
* Refactoring to use native Ubuntu Java libraries. 
* debian/control:
  - use openjdk instead of sun's java
  - update maintainer
* debian/etherpad.init.orig, debian/etherpad.upstart:
  - move the init script out of the way
  - create a basic upstart script
  - note that the open office document conversion daemon was dropped
    from the upstart configuration; if this behavior is desired, please
    create a separate upstart job for it
* debian/rules:
  - just use basic dh_installinit, as it will pick up the new upstart job
* New release
* Changed maintainer to Packaging
* Fixed installation scripts
* Initial Release.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/**
 
2
 * Copyright 2009 Google Inc.
 
3
 * 
 
4
 * Licensed under the Apache License, Version 2.0 (the "License");
 
5
 * you may not use this file except in compliance with the License.
 
6
 * You may obtain a copy of the License at
 
7
 * 
 
8
 *      http://www.apache.org/licenses/LICENSE-2.0
 
9
 * 
 
10
 * Unless required by applicable law or agreed to in writing, software
 
11
 * distributed under the License is distributed on an "AS-IS" BASIS,
 
12
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 
13
 * See the License for the specific language governing permissions and
 
14
 * limitations under the License.
 
15
 */
 
16
 
 
17
 
 
18
undoModule = (function() {
 
19
  var stack = (function() {
 
20
    var stackElements = [];
 
21
    // two types of stackElements:
 
22
    // 1) { elementType: UNDOABLE_EVENT, eventType: "anything", [backset: <changeset>,]
 
23
    //      [selStart: <char number>, selEnd: <char number>, selFocusAtStart: <boolean>] }
 
24
    // 2) { elementType: EXTERNAL_CHANGE, changeset: <changeset> }
 
25
    // invariant: no two consecutive EXTERNAL_CHANGEs
 
26
    var numUndoableEvents = 0;
 
27
 
 
28
    var UNDOABLE_EVENT = "undoableEvent";
 
29
    var EXTERNAL_CHANGE = "externalChange";
 
30
 
 
31
    function clearStack() {
 
32
      stackElements.length = 0;
 
33
      stackElements.push({ elementType: UNDOABLE_EVENT, eventType: "bottom" });
 
34
      numUndoableEvents = 1;
 
35
    }
 
36
    clearStack();
 
37
 
 
38
    function pushEvent(event) {
 
39
      var e = extend({}, event);
 
40
      e.elementType = UNDOABLE_EVENT;
 
41
      stackElements.push(e);
 
42
      numUndoableEvents++;
 
43
      //dmesg("pushEvent backset: "+event.backset);
 
44
    }
 
45
 
 
46
    function pushExternalChange(cs) {
 
47
      var idx = stackElements.length-1;
 
48
      if (stackElements[idx].elementType == EXTERNAL_CHANGE) {
 
49
        stackElements[idx].changeset = Changeset.compose(stackElements[idx].changeset, cs, getAPool());
 
50
      }
 
51
      else {
 
52
        stackElements.push({elementType: EXTERNAL_CHANGE, changeset: cs});
 
53
      }
 
54
    }
 
55
 
 
56
    function _exposeEvent(nthFromTop) {
 
57
      // precond: 0 <= nthFromTop < numUndoableEvents
 
58
      var targetIndex = stackElements.length - 1 - nthFromTop;
 
59
      var idx = stackElements.length - 1;
 
60
      while (idx > targetIndex || stackElements[idx].elementType == EXTERNAL_CHANGE) {
 
61
        if (stackElements[idx].elementType == EXTERNAL_CHANGE) {
 
62
          var ex = stackElements[idx];
 
63
          var un = stackElements[idx-1];
 
64
          if (un.backset) {
 
65
            var excs = ex.changeset;
 
66
            var unbs = un.backset;
 
67
            un.backset = Changeset.follow(excs, un.backset, false, getAPool());
 
68
            ex.changeset = Changeset.follow(unbs, ex.changeset, true, getAPool());
 
69
            if ((typeof un.selStart) == "number") {
 
70
              var newSel = Changeset.characterRangeFollow(excs, un.selStart, un.selEnd);
 
71
              un.selStart = newSel[0];
 
72
              un.selEnd = newSel[1];
 
73
              if (un.selStart == un.selEnd) {
 
74
                un.selFocusAtStart = false;
 
75
              }
 
76
            }
 
77
          }
 
78
          stackElements[idx-1] = ex;
 
79
          stackElements[idx] = un;
 
80
          if (idx >= 2 && stackElements[idx-2].elementType == EXTERNAL_CHANGE) {
 
81
            ex.changeset = Changeset.compose(stackElements[idx-2].changeset,
 
82
                                             ex.changeset, getAPool());
 
83
            stackElements.splice(idx-2, 1);
 
84
            idx--;
 
85
          }
 
86
        }
 
87
        else {
 
88
          idx--;
 
89
        }
 
90
      }
 
91
    }
 
92
 
 
93
    function getNthFromTop(n) {
 
94
      // precond: 0 <= n < numEvents()
 
95
      _exposeEvent(n);
 
96
      return stackElements[stackElements.length - 1 - n];
 
97
    }
 
98
 
 
99
    function numEvents() {
 
100
      return numUndoableEvents;
 
101
    }
 
102
 
 
103
    function popEvent() {
 
104
      // precond: numEvents() > 0
 
105
      _exposeEvent(0);
 
106
      numUndoableEvents--;
 
107
      return stackElements.pop();
 
108
    }
 
109
 
 
110
    return {numEvents:numEvents, popEvent:popEvent, pushEvent: pushEvent,
 
111
            pushExternalChange: pushExternalChange, clearStack: clearStack,
 
112
            getNthFromTop:getNthFromTop};
 
113
  })();
 
114
 
 
115
  // invariant: stack always has at least one undoable event
 
116
 
 
117
  var undoPtr = 0; // zero-index from top of stack, 0 == top
 
118
 
 
119
  function clearHistory() {
 
120
    stack.clearStack();
 
121
    undoPtr = 0;
 
122
  }
 
123
 
 
124
  function _charOccurrences(str, c) {
 
125
    var i = 0;
 
126
    var count = 0;
 
127
    while (i >= 0 && i < str.length) {
 
128
      i = str.indexOf(c, i);
 
129
      if (i >= 0) {
 
130
        count++;
 
131
        i++;
 
132
      }
 
133
    }
 
134
    return count;
 
135
  }
 
136
 
 
137
  function _opcodeOccurrences(cs, opcode) {
 
138
    return _charOccurrences(Changeset.unpack(cs).ops, opcode);
 
139
  }
 
140
 
 
141
  function _mergeChangesets(cs1, cs2) {
 
142
    if (! cs1) return cs2;
 
143
    if (! cs2) return cs1;
 
144
 
 
145
    // Rough heuristic for whether changesets should be considered one action:
 
146
    // each does exactly one insertion, no dels, and the composition does also; or
 
147
    // each does exactly one deletion, no ins, and the composition does also.
 
148
    // A little weird in that it won't merge "make bold" with "insert char"
 
149
    // but will merge "make bold and insert char" with "insert char",
 
150
    // though that isn't expected to come up.
 
151
    var plusCount1 = _opcodeOccurrences(cs1, '+');
 
152
    var plusCount2 = _opcodeOccurrences(cs2, '+');
 
153
    var minusCount1 = _opcodeOccurrences(cs1, '-');
 
154
    var minusCount2 = _opcodeOccurrences(cs2, '-');
 
155
    if (plusCount1 == 1 && plusCount2 == 1 && minusCount1 == 0 && minusCount2 == 0) {
 
156
      var merge = Changeset.compose(cs1, cs2, getAPool());
 
157
      var plusCount3 = _opcodeOccurrences(merge, '+');
 
158
      var minusCount3 = _opcodeOccurrences(merge, '-');
 
159
      if (plusCount3 == 1 && minusCount3 == 0) {
 
160
        return merge;
 
161
      }
 
162
    }
 
163
    else if (plusCount1 == 0 && plusCount2 == 0 && minusCount1 == 1 && minusCount2 == 1) {
 
164
      var merge = Changeset.compose(cs1, cs2, getAPool());
 
165
      var plusCount3 = _opcodeOccurrences(merge, '+');
 
166
      var minusCount3 = _opcodeOccurrences(merge, '-');
 
167
      if (plusCount3 == 0 && minusCount3 == 1) {
 
168
        return merge;
 
169
      }
 
170
    }
 
171
    return null;
 
172
  }
 
173
 
 
174
  function reportEvent(event) {
 
175
    var topEvent = stack.getNthFromTop(0);
 
176
 
 
177
    function applySelectionToTop() {
 
178
      if ((typeof event.selStart) == "number") {
 
179
        topEvent.selStart = event.selStart;
 
180
        topEvent.selEnd = event.selEnd;
 
181
        topEvent.selFocusAtStart = event.selFocusAtStart;
 
182
      }
 
183
    }
 
184
 
 
185
    if ((! event.backset) || Changeset.isIdentity(event.backset)) {
 
186
      applySelectionToTop();
 
187
    }
 
188
    else {
 
189
      var merged = false;
 
190
      if (topEvent.eventType == event.eventType) {
 
191
        var merge = _mergeChangesets(event.backset, topEvent.backset);
 
192
        if (merge) {
 
193
          topEvent.backset = merge;
 
194
          //dmesg("reportEvent merge: "+merge);
 
195
          applySelectionToTop();
 
196
          merged = true;
 
197
        }
 
198
      }
 
199
      if (! merged) {
 
200
        stack.pushEvent(event);
 
201
      }
 
202
      undoPtr = 0;
 
203
    }
 
204
 
 
205
  }
 
206
 
 
207
  function reportExternalChange(changeset) {
 
208
    if (changeset && ! Changeset.isIdentity(changeset)) {
 
209
      stack.pushExternalChange(changeset);
 
210
    }
 
211
  }
 
212
 
 
213
  function _getSelectionInfo(event) {
 
214
    if ((typeof event.selStart) != "number") {
 
215
      return null;
 
216
    }
 
217
    else {
 
218
      return {selStart: event.selStart, selEnd: event.selEnd,
 
219
              selFocusAtStart: event.selFocusAtStart};
 
220
    }
 
221
  }
 
222
 
 
223
  // For "undo" and "redo", the change event must be returned
 
224
  // by eventFunc and NOT reported through the normal mechanism.
 
225
  // "eventFunc" should take a changeset and an optional selection info object,
 
226
  // or can be called with no arguments to mean that no undo is possible.
 
227
  // "eventFunc" will be called exactly once.
 
228
 
 
229
  function performUndo(eventFunc) {
 
230
    if (undoPtr < stack.numEvents()-1) {
 
231
      var backsetEvent = stack.getNthFromTop(undoPtr);
 
232
      var selectionEvent = stack.getNthFromTop(undoPtr+1);
 
233
      var undoEvent = eventFunc(backsetEvent.backset, _getSelectionInfo(selectionEvent));
 
234
      stack.pushEvent(undoEvent);
 
235
      undoPtr += 2;
 
236
    }
 
237
    else eventFunc();
 
238
  }
 
239
 
 
240
  function performRedo(eventFunc) {
 
241
    if (undoPtr >= 2) {
 
242
      var backsetEvent = stack.getNthFromTop(0);
 
243
      var selectionEvent = stack.getNthFromTop(1);
 
244
      eventFunc(backsetEvent.backset, _getSelectionInfo(selectionEvent));
 
245
      stack.popEvent();
 
246
      undoPtr -= 2;
 
247
    }
 
248
    else eventFunc();
 
249
  }
 
250
 
 
251
  function getAPool() {
 
252
    return undoModule.apool;
 
253
  }
 
254
 
 
255
  return {clearHistory:clearHistory, reportEvent:reportEvent, reportExternalChange:reportExternalChange,
 
256
          performUndo:performUndo, performRedo:performRedo, enabled: true,
 
257
          apool: null}; // apool is filled in by caller
 
258
})();
 
 
b'\\ No newline at end of file'