Index: trunk/parsers/wikidom/tests/transactions/index.html |
— | — | @@ -0,0 +1,24 @@ |
| 2 | +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" |
| 3 | + "http://www.w3.org/TR/html4/loose.dtd"> |
| 4 | +<html> |
| 5 | + <head> |
| 6 | + <title>Content Transaction Tests</title> |
| 7 | + <link rel="stylesheet" href="../../lib/qunit.css" type="text/css" /> |
| 8 | + </head> |
| 9 | + <body> |
| 10 | + <h1 id="qunit-header">Content Transaction Tests</h1> |
| 11 | + <h2 id="qunit-banner"></h2> |
| 12 | + <h2 id="qunit-userAgent"></h2> |
| 13 | + <ol id="qunit-tests"></ol> |
| 14 | + <script src="../../lib/es/es.js"></script> |
| 15 | + <script src="../../lib/es/es.EventEmitter.js"></script> |
| 16 | + <script src="../../lib/es/es.Content.js"></script> |
| 17 | + <script src="../../lib/es/es.Content.Operation.js"></script> |
| 18 | + <script src="../../lib/es/es.Content.Selection.js"></script> |
| 19 | + <script src="../../lib/es/es.Content.Transaction.js"></script> |
| 20 | + <script src="../../lib/es/es.Range.js"></script> |
| 21 | + <script src="../../lib/jquery.js"></script> |
| 22 | + <script src="../../lib/qunit.js"></script> |
| 23 | + <script src="test.js"></script> |
| 24 | + </body> |
| 25 | +</html> |
Property changes on: trunk/parsers/wikidom/tests/transactions/index.html |
___________________________________________________________________ |
Added: svn:mime-type |
1 | 26 | + text/plain |
Index: trunk/parsers/wikidom/tests/transactions/test.js |
— | — | @@ -0,0 +1,56 @@ |
| 2 | +module( 'Content Transactions' ); |
| 3 | + |
| 4 | +var lines = [ |
| 5 | + { |
| 6 | + "text": "This is a test paragraph!", |
| 7 | + "annotations": [ |
| 8 | + // Make "This" italic |
| 9 | + { |
| 10 | + "type": "italic", |
| 11 | + "range": { |
| 12 | + "start": 0, |
| 13 | + "end": 4 |
| 14 | + } |
| 15 | + }, |
| 16 | + // Make "a test" a link |
| 17 | + { |
| 18 | + "type": "xlink", |
| 19 | + "data": { |
| 20 | + "url": "http://www.a.com" |
| 21 | + }, |
| 22 | + "range": { |
| 23 | + "start": 8, |
| 24 | + "end": 14 |
| 25 | + } |
| 26 | + }, |
| 27 | + // Make "test" bold |
| 28 | + { |
| 29 | + "type": "bold", |
| 30 | + "range": { |
| 31 | + "start": 10, |
| 32 | + "end": 14 |
| 33 | + } |
| 34 | + } |
| 35 | + ] |
| 36 | + } |
| 37 | +]; |
| 38 | + |
| 39 | +var content = es.Content.newFromWikiDomLines( lines ); |
| 40 | + |
| 41 | +/* Tests */ |
| 42 | + |
| 43 | +test( 'Insert and delete', 2, function() { |
| 44 | + var Op = es.Content.Operation, |
| 45 | + Tx = es.Content.Transaction; |
| 46 | + |
| 47 | + var tx = new Tx( |
| 48 | + content, |
| 49 | + new Op( 'retain', 5 ), |
| 50 | + new Op( 'insert', 'used to be' ), |
| 51 | + new Op( 'delete', 2 ), |
| 52 | + new Op( 'retain', 18 ) |
| 53 | + ); |
| 54 | + |
| 55 | + equal( tx.commit( content ).getText(), 'This used to be a test paragraph!', 'Committing' ); |
| 56 | + equal( tx.rollback( content ).getText(), 'This is a test paragraph!', 'Rolling back' ); |
| 57 | +} ); |
Property changes on: trunk/parsers/wikidom/tests/transactions/test.js |
___________________________________________________________________ |
Added: svn:mime-type |
1 | 58 | + text/plain |
Added: svn:eol-style |
2 | 59 | + native |
Index: trunk/parsers/wikidom/lib/es/es.Content.Transaction.js |
— | — | @@ -6,55 +6,81 @@ |
7 | 7 | * @param operations {Array} List of operations |
8 | 8 | * @property operations {Array} List of operations |
9 | 9 | */ |
10 | | -es.Content.Transaction = function( operations ) { |
11 | | - this.operations = operations || []; |
| 10 | +es.Content.Transaction = function( content, operations ) { |
| 11 | + this.content = content; |
| 12 | + this.operations = []; |
| 13 | + if ( arguments.length > 1 ) { |
| 14 | + var range = new es.Range(); |
| 15 | + for ( var i = 1; i < arguments.length; i++ ) { |
| 16 | + var operation = arguments[i]; |
| 17 | + switch ( operation.getType() ) { |
| 18 | + case 'retain': |
| 19 | + case 'delete': |
| 20 | + range.to = range.from + operation.getLength(); |
| 21 | + if ( !operation.hasContent() ) { |
| 22 | + operation.setContent( content.getContent( range ) ); |
| 23 | + } |
| 24 | + range.from = range.to; |
| 25 | + break; |
| 26 | + } |
| 27 | + this.operations.push( operation ); |
| 28 | + } |
| 29 | + } |
12 | 30 | }; |
13 | 31 | |
14 | 32 | es.Content.Transaction.prototype.add = function( operation ) { |
15 | 33 | this.operations.push( operation ); |
16 | 34 | }; |
17 | 35 | |
18 | | -es.Content.Transaction.prototype.commit = function( from ) { |
| 36 | +es.Content.Transaction.prototype.commit = function() { |
19 | 37 | var range = new es.Range(), |
20 | 38 | to = new es.Content(); |
21 | 39 | for ( var i = 0; i < this.operations.length; i++ ) { |
22 | 40 | var operation = this.operations[i]; |
23 | | - range.to = range.from + operation.getLength(); |
24 | 41 | switch (operation.getType()) { |
25 | 42 | case 'retain': |
26 | | - to.insert( to.getLength(), from.getData( range ) ); |
| 43 | + range.to = range.from + operation.getLength(); |
| 44 | + // Automatically add content to operation |
| 45 | + if ( !operation.hasContent() ) { |
| 46 | + operation.setContent( this.content.getContent( range ) ); |
| 47 | + } |
| 48 | + to.insert( to.getLength(), operation.getContent().getData() ); |
27 | 49 | range.from = range.to; |
28 | 50 | break; |
29 | 51 | case 'insert': |
30 | | - to.insert( to.getLength(), operation.getContent() ); |
| 52 | + to.insert( to.getLength(), operation.getContent().getData() ); |
31 | 53 | break; |
32 | 54 | case 'delete': |
33 | | - // TODO: Validate operation.getContent against content being skipped |
34 | | - offset += operation.getLength(); |
| 55 | + range.to = range.from + operation.getLength(); |
| 56 | + // Automatically add content to operation |
| 57 | + if ( !operation.hasContent() ) { |
| 58 | + operation.setContent( this.content.getContent( range ) ); |
| 59 | + } |
| 60 | + range.from = range.to; |
35 | 61 | break; |
36 | 62 | } |
37 | 63 | } |
38 | 64 | return to; |
39 | 65 | }; |
40 | 66 | |
41 | | -es.Content.Transaction.prototype.rollback = function( from ) { |
| 67 | +es.Content.Transaction.prototype.rollback = function() { |
42 | 68 | var range = new es.Range(), |
43 | 69 | to = new es.Content(); |
44 | 70 | for ( var i = 0; i < this.operations.length; i++ ) { |
45 | 71 | var operation = this.operations[i]; |
46 | | - range.to = range.from + operation.getLength(); |
47 | 72 | switch (operation.getType()) { |
48 | 73 | case 'retain': |
49 | | - to.insert( to.getLength(), from.getData( range ) ); |
| 74 | + range.to = range.from + operation.getLength(); |
| 75 | + to.insert( to.getLength(), operation.getContent().getData() ); |
50 | 76 | range.from = range.to; |
51 | 77 | break; |
| 78 | + case 'delete': |
| 79 | + to.insert( to.getLength(), operation.getContent().getData() ); |
| 80 | + break; |
52 | 81 | case 'insert': |
53 | | - // TODO: Validate operation.getContent against content being skipped |
54 | | - offset += operation.getLength(); |
| 82 | + range.to = range.from + operation.getLength(); |
| 83 | + range.from = range.to; |
55 | 84 | break; |
56 | | - case 'delete': |
57 | | - to.insert( to.getLength(), operation.getContent() ); |
58 | | - break; |
59 | 85 | } |
60 | 86 | } |
61 | 87 | return to; |
Index: trunk/parsers/wikidom/lib/es/es.Content.Operation.js |
— | — | @@ -4,7 +4,7 @@ |
5 | 5 | * @class |
6 | 6 | * @constructor |
7 | 7 | * @param type {String} Type of operation, e.g. insert, delete, annotate |
8 | | - * @param content {es.Content} Content being inserted or modified |
| 8 | + * @param content {Mixed} Either {Integer} length, {String} text or {es.Content} content |
9 | 9 | * @param data {Object} Data of operation, e.g. range |
10 | 10 | * @property type {String} Type of operation |
11 | 11 | * @property content {es.Content} Content of operation |
— | — | @@ -12,8 +12,21 @@ |
13 | 13 | */ |
14 | 14 | es.Content.Operation = function( type, content, data ) { |
15 | 15 | this.type = type; |
16 | | - this.contents = contents; |
17 | | - this.data = data; |
| 16 | + switch ( typeof content ) { |
| 17 | + case 'number': |
| 18 | + this.content = null; |
| 19 | + this.length = content; |
| 20 | + break; |
| 21 | + case 'string': |
| 22 | + this.content = es.Content.newFromText( content ); |
| 23 | + this.length = this.content.getLength(); |
| 24 | + break; |
| 25 | + case 'object': |
| 26 | + this.content = content; |
| 27 | + this.length = this.content.getLength(); |
| 28 | + break; |
| 29 | + } |
| 30 | + this.data = data || {}; |
18 | 31 | }; |
19 | 32 | |
20 | 33 | es.Content.Operation.prototype.getType = function() { |
— | — | @@ -24,8 +37,16 @@ |
25 | 38 | return this.content; |
26 | 39 | }; |
27 | 40 | |
| 41 | +es.Content.Operation.prototype.setContent = function( content ) { |
| 42 | + this.content = content; |
| 43 | +}; |
| 44 | + |
| 45 | +es.Content.Operation.prototype.hasContent = function() { |
| 46 | + return !!this.content; |
| 47 | +}; |
| 48 | + |
28 | 49 | es.Content.Operation.prototype.getLength = function() { |
29 | | - return this.content.getLength();; |
| 50 | + return this.content ? this.content.getLength() : this.length; |
30 | 51 | }; |
31 | 52 | |
32 | 53 | es.Content.Operation.prototype.getData = function() { |
Index: trunk/parsers/wikidom/lib/es/es.Content.js |
— | — | @@ -90,6 +90,18 @@ |
91 | 91 | /* Static Methods */ |
92 | 92 | |
93 | 93 | /** |
| 94 | + * Creates a new Content object from plain text. |
| 95 | + * |
| 96 | + * @static |
| 97 | + * @method |
| 98 | + * @param text {String} Text to convert |
| 99 | + * @returns {es.Content} EditSurface content object containing converted text |
| 100 | + */ |
| 101 | +es.Content.newFromText = function( text ) { |
| 102 | + return new es.Content( text.split('') ); |
| 103 | +}; |
| 104 | + |
| 105 | +/** |
94 | 106 | * Creates a new Content object from a WikiDom line object. |
95 | 107 | * |
96 | 108 | * @static |