r103849 MediaWiki - Code Review archive

Repository:MediaWiki
Revision:r103848‎ | r103849 | r103850 >
Date:22:32, 21 November 2011
Author:tparscal
Status:deferred
Tags:
Comment:
* Switched to using JSON for hashing, allowing us to use the native JSON.stringify where available, which is much faster
* Added a bunch of utility functions for working with character data and annotations
* Got toolbar button states to follow selection of more than one character
Modified paths:
  • /trunk/extensions/VisualEditor/demo/es.js (modified) (history)
  • /trunk/extensions/VisualEditor/modules/es/es.Surface.css (modified) (history)
  • /trunk/extensions/VisualEditor/modules/es/es.TransactionProcessor.js (modified) (history)
  • /trunk/extensions/VisualEditor/modules/es/models/es.DocumentModel.js (modified) (history)
  • /trunk/extensions/VisualEditor/tests/es/es.DocumentModel.test.js (modified) (history)
  • /trunk/extensions/VisualEditor/tests/es/es.TransactionProcessor.test.js (modified) (history)
  • /trunk/extensions/VisualEditor/tests/es/es.testData.js (modified) (history)

Diff [purge]

Index: trunk/extensions/VisualEditor/tests/es/es.TransactionProcessor.test.js
@@ -18,8 +18,8 @@
1919 [
2020 { 'type': 'paragraph', 'attributes': { 'test': 1 } },
2121 'a',
22 - ['b', { 'type': 'textStyle/bold', 'hash': '#textStyle/bold' }],
23 - ['c', { 'type': 'textStyle/italic', 'hash': '#textStyle/italic' }],
 22+ ['b', { 'type': 'textStyle/bold', 'hash': '{"type":"textStyle/bold"}' }],
 23+ ['c', { 'type': 'textStyle/italic', 'hash': '{"type":"textStyle/italic"}' }],
2424 { 'type': '/paragraph' }
2525 ],
2626 'commit applies an element attribute change transaction to the content'
@@ -32,8 +32,8 @@
3333 [
3434 { 'type': 'paragraph' },
3535 'a',
36 - ['b', { 'type': 'textStyle/bold', 'hash': '#textStyle/bold' }],
37 - ['c', { 'type': 'textStyle/italic', 'hash': '#textStyle/italic' }],
 36+ ['b', { 'type': 'textStyle/bold', 'hash': '{"type":"textStyle/bold"}' }],
 37+ ['c', { 'type': 'textStyle/italic', 'hash': '{"type":"textStyle/italic"}' }],
3838 { 'type': '/paragraph' }
3939 ],
4040 'rollback reverses the effect of an element attribute change transaction on the content'
@@ -49,12 +49,12 @@
5050 documentModel.getData( new es.Range( 0, 5 ) ),
5151 [
5252 { 'type': 'paragraph' },
53 - ['a', { 'type': 'textStyle/bold', 'hash': '#textStyle/bold' }],
54 - ['b', { 'type': 'textStyle/bold', 'hash': '#textStyle/bold' }],
 53+ ['a', { 'type': 'textStyle/bold', 'hash': '{"type":"textStyle/bold"}' }],
 54+ ['b', { 'type': 'textStyle/bold', 'hash': '{"type":"textStyle/bold"}' }],
5555 [
5656 'c',
57 - { 'type': 'textStyle/italic', 'hash': '#textStyle/italic' },
58 - { 'type': 'textStyle/bold', 'hash': '#textStyle/bold' }
 57+ { 'type': 'textStyle/italic', 'hash': '{"type":"textStyle/italic"}' },
 58+ { 'type': 'textStyle/bold', 'hash': '{"type":"textStyle/bold"}' }
5959 ],
6060 { 'type': '/paragraph' }
6161 ],
@@ -68,8 +68,8 @@
6969 [
7070 { 'type': 'paragraph' },
7171 'a',
72 - ['b', { 'type': 'textStyle/bold', 'hash': '#textStyle/bold' }],
73 - ['c', { 'type': 'textStyle/italic', 'hash': '#textStyle/italic' }],
 72+ ['b', { 'type': 'textStyle/bold', 'hash': '{"type":"textStyle/bold"}' }],
 73+ ['c', { 'type': 'textStyle/italic', 'hash': '{"type":"textStyle/italic"}' }],
7474 { 'type': '/paragraph' }
7575 ],
7676 'rollback reverses the effect of a content annotation transaction on the content'
@@ -84,9 +84,9 @@
8585 [
8686 { 'type': 'paragraph' },
8787 'a',
88 - ['b', { 'type': 'textStyle/bold', 'hash': '#textStyle/bold' }],
 88+ ['b', { 'type': 'textStyle/bold', 'hash': '{"type":"textStyle/bold"}' }],
8989 'd',
90 - ['c', { 'type': 'textStyle/italic', 'hash': '#textStyle/italic' }],
 90+ ['c', { 'type': 'textStyle/italic', 'hash': '{"type":"textStyle/italic"}' }],
9191 { 'type': '/paragraph' }
9292 ],
9393 'commit applies an insertion transaction to the content'
@@ -97,9 +97,9 @@
9898 documentModel.getChildren()[0].getContent(),
9999 [
100100 'a',
101 - ['b', { 'type': 'textStyle/bold', 'hash': '#textStyle/bold' }],
 101+ ['b', { 'type': 'textStyle/bold', 'hash': '{"type":"textStyle/bold"}' }],
102102 'd',
103 - ['c', { 'type': 'textStyle/italic', 'hash': '#textStyle/italic' }]
 103+ ['c', { 'type': 'textStyle/italic', 'hash': '{"type":"textStyle/italic"}' }]
104104 ],
105105 'commit keeps model tree up to date with insertions'
106106 );
@@ -111,8 +111,8 @@
112112 [
113113 { 'type': 'paragraph' },
114114 'a',
115 - ['b', { 'type': 'textStyle/bold', 'hash': '#textStyle/bold' }],
116 - ['c', { 'type': 'textStyle/italic', 'hash': '#textStyle/italic' }],
 115+ ['b', { 'type': 'textStyle/bold', 'hash': '{"type":"textStyle/bold"}' }],
 116+ ['c', { 'type': 'textStyle/italic', 'hash': '{"type":"textStyle/italic"}' }],
117117 { 'type': '/paragraph' }
118118 ],
119119 'rollback reverses the effect of an insertion transaction on the content'
@@ -123,8 +123,8 @@
124124 documentModel.getChildren()[0].getContent(),
125125 [
126126 'a',
127 - ['b', { 'type': 'textStyle/bold', 'hash': '#textStyle/bold' }],
128 - ['c', { 'type': 'textStyle/italic', 'hash': '#textStyle/italic' }]
 127+ ['b', { 'type': 'textStyle/bold', 'hash': '{"type":"textStyle/bold"}' }],
 128+ ['c', { 'type': 'textStyle/italic', 'hash': '{"type":"textStyle/italic"}' }]
129129 ],
130130 'rollback keeps model tree up to date with insertions'
131131 );
@@ -157,8 +157,8 @@
158158 [
159159 { 'type': 'paragraph' },
160160 'a',
161 - ['b', { 'type': 'textStyle/bold', 'hash': '#textStyle/bold' }],
162 - ['c', { 'type': 'textStyle/italic', 'hash': '#textStyle/italic' }],
 161+ ['b', { 'type': 'textStyle/bold', 'hash': '{"type":"textStyle/bold"}' }],
 162+ ['c', { 'type': 'textStyle/italic', 'hash': '{"type":"textStyle/italic"}' }],
163163 { 'type': '/paragraph' }
164164 ],
165165 'rollback reverses the effect of a removal transaction on the content'
@@ -169,8 +169,8 @@
170170 documentModel.getChildren()[0].getContent(),
171171 [
172172 'a',
173 - ['b', { 'type': 'textStyle/bold', 'hash': '#textStyle/bold' }],
174 - ['c', { 'type': 'textStyle/italic', 'hash': '#textStyle/italic' }]
 173+ ['b', { 'type': 'textStyle/bold', 'hash': '{"type":"textStyle/bold"}' }],
 174+ ['c', { 'type': 'textStyle/italic', 'hash': '{"type":"textStyle/italic"}' }]
175175 ],
176176 'rollback keeps model tree up to date with removals'
177177 );
@@ -188,8 +188,8 @@
189189 'a',
190190 { 'type': '/paragraph' },
191191 { 'type': 'paragraph' },
192 - ['b', { 'type': 'textStyle/bold', 'hash': '#textStyle/bold' }],
193 - ['c', { 'type': 'textStyle/italic', 'hash': '#textStyle/italic' }],
 192+ ['b', { 'type': 'textStyle/bold', 'hash': '{"type":"textStyle/bold"}' }],
 193+ ['c', { 'type': 'textStyle/italic', 'hash': '{"type":"textStyle/italic"}' }],
194194 { 'type': '/paragraph' }
195195 ],
196196 'commit applies an insertion transaction that splits the paragraph'
@@ -206,8 +206,8 @@
207207 deepEqual(
208208 documentModel.getChildren()[1].getContent(),
209209 [
210 - ['b', { 'type': 'textStyle/bold', 'hash': '#textStyle/bold' }],
211 - ['c', { 'type': 'textStyle/italic', 'hash': '#textStyle/italic' }]
 210+ ['b', { 'type': 'textStyle/bold', 'hash': '{"type":"textStyle/bold"}' }],
 211+ ['c', { 'type': 'textStyle/italic', 'hash': '{"type":"textStyle/italic"}' }]
212212 ],
213213 'commit keeps model tree up to date with paragraph split (paragraph 2)'
214214 );
@@ -219,8 +219,8 @@
220220 [
221221 { 'type': 'paragraph' },
222222 'a',
223 - ['b', { 'type': 'textStyle/bold', 'hash': '#textStyle/bold' }],
224 - ['c', { 'type': 'textStyle/italic', 'hash': '#textStyle/italic' }],
 223+ ['b', { 'type': 'textStyle/bold', 'hash': '{"type":"textStyle/bold"}' }],
 224+ ['c', { 'type': 'textStyle/italic', 'hash': '{"type":"textStyle/italic"}' }],
225225 { 'type': '/paragraph' }
226226 ],
227227 'rollback reverses the effect of a paragraph split on the content'
@@ -231,8 +231,8 @@
232232 documentModel.getChildren()[0].getContent(),
233233 [
234234 'a',
235 - ['b', { 'type': 'textStyle/bold', 'hash': '#textStyle/bold' }],
236 - ['c', { 'type': 'textStyle/italic', 'hash': '#textStyle/italic' }]
 235+ ['b', { 'type': 'textStyle/bold', 'hash': '{"type":"textStyle/bold"}' }],
 236+ ['c', { 'type': 'textStyle/italic', 'hash': '{"type":"textStyle/italic"}' }]
237237 ],
238238 'rollback keeps model tree up to date with paragraph split (paragraphs are merged back)'
239239 );
Index: trunk/extensions/VisualEditor/tests/es/es.testData.js
@@ -144,9 +144,9 @@
145145 // 1 - Plain content
146146 'a',
147147 // 2 - Annotated content
148 - ['b', { 'type': 'textStyle/bold', 'hash': '#textStyle/bold' }],
 148+ ['b', { 'type': 'textStyle/bold', 'hash': '{"type":"textStyle/bold"}' }],
149149 // 3 - Annotated content
150 - ['c', { 'type': 'textStyle/italic', 'hash': '#textStyle/italic' }],
 150+ ['c', { 'type': 'textStyle/italic', 'hash': '{"type":"textStyle/italic"}' }],
151151 // 4 - End of paragraph
152152 { 'type': '/paragraph' },
153153 // 5 - Beginning of table
Index: trunk/extensions/VisualEditor/tests/es/es.DocumentModel.test.js
@@ -93,8 +93,8 @@
9494 deepEqual(
9595 childNodes[0].getContent( new es.Range( 1, 3 ) ),
9696 [
97 - ['b', { 'type': 'textStyle/bold', 'hash': '#textStyle/bold' }],
98 - ['c', { 'type': 'textStyle/italic', 'hash': '#textStyle/italic' }]
 97+ ['b', { 'type': 'textStyle/bold', 'hash': '{"type":"textStyle/bold"}' }],
 98+ ['c', { 'type': 'textStyle/italic', 'hash': '{"type":"textStyle/italic"}' }]
9999 ],
100100 'getContent can return an ending portion of the content'
101101 );
@@ -102,14 +102,14 @@
103103 // Test 2
104104 deepEqual(
105105 childNodes[0].getContent( new es.Range( 0, 2 ) ),
106 - ['a', ['b', { 'type': 'textStyle/bold', 'hash': '#textStyle/bold' }]],
 106+ ['a', ['b', { 'type': 'textStyle/bold', 'hash': '{"type":"textStyle/bold"}' }]],
107107 'getContent can return a beginning portion of the content'
108108 );
109109
110110 // Test 3
111111 deepEqual(
112112 childNodes[0].getContent( new es.Range( 1, 2 ) ),
113 - [['b', { 'type': 'textStyle/bold', 'hash': '#textStyle/bold' }]],
 113+ [['b', { 'type': 'textStyle/bold', 'hash': '{"type":"textStyle/bold"}' }]],
114114 'getContent can return a middle portion of the content'
115115 );
116116
@@ -134,9 +134,9 @@
135135 test( 'es.DocumentModel.getIndexOfAnnotation', 3, function() {
136136 var documentModel = es.DocumentModel.newFromPlainObject( esTest.obj );
137137
138 - var bold = { 'type': 'textStyle/bold', 'hash': '#textStyle/bold' },
139 - italic = { 'type': 'textStyle/italic', 'hash': '#textStyle/italic' },
140 - nothing = { 'type': 'nothing', 'hash': '#nothing' },
 138+ var bold = { 'type': 'textStyle/bold', 'hash': '{"type":"textStyle/bold"}' },
 139+ italic = { 'type': 'textStyle/italic', 'hash': '{"type":"textStyle/italic"}' },
 140+ nothing = { 'type': 'nothing', 'hash': '{"type":"nothing"}' },
141141 character = ['a', bold, italic];
142142
143143 // Test 1
@@ -198,12 +198,12 @@
199199 );
200200 deepEqual(
201201 documentModel.getAnnotationsFromOffset( 2 ),
202 - [{ 'type': 'textStyle/bold', 'hash': '#textStyle/bold' }],
 202+ [{ 'type': 'textStyle/bold', 'hash': '{"type":"textStyle/bold"}' }],
203203 'getAnnotationsFromOffset returns annotations of annotated content correctly'
204204 );
205205 deepEqual(
206206 documentModel.getAnnotationsFromOffset( 3 ),
207 - [{ 'type': 'textStyle/italic', 'hash': '#textStyle/italic' }],
 207+ [{ 'type': 'textStyle/italic', 'hash': '{"type":"textStyle/italic"}' }],
208208 'getAnnotationsFromOffset returns annotations of annotated content correctly'
209209 );
210210 deepEqual(
@@ -272,28 +272,28 @@
273273 'type': 'annotate',
274274 'method': 'set',
275275 'bias': 'start',
276 - 'annotation': { 'type': 'textStyle/bold', 'hash': '#textStyle/bold' }
 276+ 'annotation': { 'type': 'textStyle/bold', 'hash': '{"type":"textStyle/bold"}' }
277277 },
278278 { 'type': 'retain', 'length': 1 },
279279 {
280280 'type': 'annotate',
281281 'method': 'set',
282282 'bias': 'stop',
283 - 'annotation': { 'type': 'textStyle/bold', 'hash': '#textStyle/bold' }
 283+ 'annotation': { 'type': 'textStyle/bold', 'hash': '{"type":"textStyle/bold"}' }
284284 },
285285 { 'type': 'retain', 'length': 1 },
286286 {
287287 'type': 'annotate',
288288 'method': 'set',
289289 'bias': 'start',
290 - 'annotation': { 'type': 'textStyle/bold', 'hash': '#textStyle/bold' }
 290+ 'annotation': { 'type': 'textStyle/bold', 'hash': '{"type":"textStyle/bold"}' }
291291 },
292292 { 'type': 'retain', 'length': 1 },
293293 {
294294 'type': 'annotate',
295295 'method': 'set',
296296 'bias': 'stop',
297 - 'annotation': { 'type': 'textStyle/bold', 'hash': '#textStyle/bold' }
 297+ 'annotation': { 'type': 'textStyle/bold', 'hash': '{"type":"textStyle/bold"}' }
298298 },
299299 { 'type': 'retain', 'length': 30 }
300300 ],
@@ -313,8 +313,8 @@
314314 'type': 'remove',
315315 'data': [
316316 'a',
317 - ['b', { 'type': 'textStyle/bold', 'hash': '#textStyle/bold' }],
318 - ['c', { 'type': 'textStyle/italic', 'hash': '#textStyle/italic' }]
 317+ ['b', { 'type': 'textStyle/bold', 'hash': '{"type":"textStyle/bold"}' }],
 318+ ['c', { 'type': 'textStyle/italic', 'hash': '{"type":"textStyle/italic"}' }]
319319 ]
320320 },
321321 { 'type': 'retain', 'length': 30 }
@@ -350,7 +350,7 @@
351351 {
352352 'type': 'remove',
353353 'data': [
354 - ['c', { 'type': 'textStyle/italic', 'hash': '#textStyle/italic' }]
 354+ ['c', { 'type': 'textStyle/italic', 'hash': '{"type":"textStyle/italic"}' }]
355355 ]
356356 },
357357 { 'type': 'retain', 'length': 30 }
@@ -365,7 +365,7 @@
366366 { 'type': 'retain', 'length': 3 },
367367 {
368368 'type': 'remove',
369 - 'data': [['c', { 'type': 'textStyle/italic', 'hash': '#textStyle/italic' }]]
 369+ 'data': [['c', { 'type': 'textStyle/italic', 'hash': '{"type":"textStyle/italic"}' }]]
370370 },
371371 { 'type': 'retain', 'length': 4 },
372372 {
@@ -400,7 +400,7 @@
401401 { 'type': 'retain', 'length': 3 },
402402 {
403403 'type': 'remove',
404 - 'data': [['c', { 'type': 'textStyle/italic', 'hash': '#textStyle/italic' }]]
 404+ 'data': [['c', { 'type': 'textStyle/italic', 'hash': '{"type":"textStyle/italic"}' }]]
405405 },
406406 { 'type': 'retain', 'length': 4 },
407407 {
Index: trunk/extensions/VisualEditor/demo/es.js
@@ -3,106 +3,132 @@
44 'type': 'document',
55 'children': [
66 {
7 - 'type': 'paragraph',
8 - 'content': { 'text': 'Barack Hussein Obama II (born August 4 1961) is the 44th and current president of the United States of America. Obama won the 2008 United States presidential election on November 4. He became the first African-American president when he was inaugurated on January 20, 2009.' }
9 - },
10 - {
117 'type': 'heading',
12 - 'attributes': { 'level': 2 },
13 - 'content': { 'text': 'Before becoming president' }
 8+ 'attributes': { 'level': 1 },
 9+ 'content': { 'text': 'Direct manipulation interface' }
1410 },
1511 {
1612 'type': 'paragraph',
17 - 'content': { 'text': 'Obama was born in Honolulu, Hawaii. His father was a black foreign student from Kenya and his mother was a white woman from Kansas. He spent most of his childhood there, although he lived in Indonesia with his mother and stepfather from age 6 to age 10. He moved back to Hawaii after that to live with his grandparents. He started college at Occidental College in Los Angeles, and graduated from Columbia University in New York City. After taking time off to community organize, Obama went to law school at Harvard University. After law school, Obama worked for a law firm. The law firm sued companies who fired people, and sued the government claiming that some black people were prevented from voting.' }
 13+ 'content': {
 14+ 'text': 'In computer science, direct manipulation is a human-computer interaction style which involves continuous representation of objects of interest, and rapid, reversible, incremental actions and feedback. The intention is to allow a user to directly manipulate objects presented to them, using actions that correspond at least loosely to the physical world. An example of direct-manipulation is resizing a graphical shape, such as a rectangle, by dragging its corners or edges with a mouse.',
 15+ 'annotations': [
 16+ {
 17+ 'type': 'link/internal',
 18+ 'data': {
 19+ 'title': 'Computer_science'
 20+ },
 21+ 'range': {
 22+ 'start': 3,
 23+ 'end': 19
 24+ }
 25+ },
 26+ {
 27+ 'type': 'link/internal',
 28+ 'data': {
 29+ 'title': 'Human-computer interaction'
 30+ },
 31+ 'range': {
 32+ 'start': 46,
 33+ 'end': 72
 34+ }
 35+ }
 36+ ]
 37+ }
1838 },
1939 {
2040 'type': 'paragraph',
21 - 'content': { 'text': 'Later, Obama worked for Alice Palmer, an Illinois state senator. She ran for U.S. Congress. While doing so, Obama became interested in her Illinois state senate seat. Later, Alice Palmer tried to run for that state senate seat, but Obama got her name taken off the ballot because she had not met the rules for the election. Because of this, he ran unopposed (without anybody else running against him) in the election. He ran for election and became an Illinois state senator. He was state senator from 1997 to 2004.' }
 41+ 'content': { 'text': 'Having real-world metaphors for objects and actions can make it easier for a user to learn and use an interface (some might say that the interface is more natural or intuitive), and rapid, incremental feedback allows a user to make fewer errors and complete tasks in less time, because they can see the results of an action before completing the action, thus evaluating the output and compensating for mistakes.' }
2242 },
2343 {
2444 'type': 'paragraph',
25 - 'content': { 'text': 'While he was Illinois state senator, he was known for writing a law that required police to keep records on the race of people they stopped. The law that he wrote also forced police to videotape when they talked to people they suspect of murder. He also taught law part time at the University of Chicago Law School.' }
 45+ 'content': {
 46+ 'text': 'The term was introduced by Ben Shneiderman in 1983 within the context of office applications and the desktop metaphor. Individuals in academia and computer scientists doing research on future user interfaces often put as much or even more stress on tactile control and feedback, or sonic control and feedback than on the visual feedback given by most GUIs. As a result the term direct manipulation interface has been more widespread in these environments. ',
 47+ 'annotations': [
 48+ {
 49+ 'type': 'link/internal',
 50+ 'data': {
 51+ 'title': 'Ben_Shneiderman'
 52+ },
 53+ 'range': {
 54+ 'start': 27,
 55+ 'end': 42
 56+ }
 57+ },
 58+ {
 59+ 'type': 'link/internal',
 60+ 'data': {
 61+ 'title': 'GUI'
 62+ },
 63+ 'range': {
 64+ 'start': 352,
 65+ 'end': 356
 66+ }
 67+ },
 68+ {
 69+ 'type': 'object/hook',
 70+ 'data': {
 71+ 'html': '<sup><small><a href="#">[1]</a></small><sup>'
 72+ },
 73+ 'range': {
 74+ 'start': 118,
 75+ 'end': 119
 76+ }
 77+ },
 78+ {
 79+ 'type': 'object/template',
 80+ 'data': {
 81+ 'html': '<sup><small>[<a href="#">citation needed</a>]</small><sup>'
 82+ },
 83+ 'range': {
 84+ 'start': 456,
 85+ 'end': 457
 86+ }
 87+ }
 88+ ]
 89+ }
2690 },
2791 {
28 - 'type': 'paragraph',
29 - 'content': { 'text': 'Obama later ran for the U.S. Senate. While running for Senate, John Kerry asked him to speak at the Democratic National Convention. Most people had never heard of Obama, but many people saw him speak on television and he became well known after that.' }
30 - },
31 - {
32 - 'type': 'paragraph',
33 - 'content': { 'text': 'Obama won the presidential election of 2008. He was a U.S. Senator from 2005 to 2008.' }
34 - },
35 - {
3692 'type': 'heading',
3793 'attributes': { 'level': 2 },
38 - 'content': { 'text': 'Presidential campaign' }
 94+ 'content': { 'text': 'In contrast to WIMP/GUI interfaces' }
3995 },
4096 {
4197 'type': 'paragraph',
42 - 'content': { 'text': 'Barack Obama\'s presidential campaign for the White House started in early June 2008 when he defeated Hillary Clinton in the 2008 Democratic primaries. Hillary Clinton was favored to win but Obama won many smaller state caucuses (local party elections) by having a lot of volunteers. He decided not to accept government money for his campaign so that he could accept more money from people. He raised the most amount of money ever for a presidential campaign.' }
 98+ 'content': {
 99+ 'text': 'Direct manipulation is closely associated with interfaces that use windows, icons, menus, and a pointing device (WIMP GUI) as these almost always incorporate direct manipulation to at least some degree. However, direct manipulation should not be confused with these other terms, as it does not imply the use of windows or even graphical output. For example, direct manipulation concepts can be applied to interfaces for blind or vision-impaired users, using a combination of tactile and sonic devices and software.',
 100+ 'annotations': [
 101+ {
 102+ 'type': 'link/internal',
 103+ 'data': {
 104+ 'title': 'WIMP_(computing)'
 105+ },
 106+ 'range': {
 107+ 'start': 113,
 108+ 'end': 117
 109+ }
 110+ }
 111+ ]
 112+ }
43113 },
44114 {
45115 'type': 'paragraph',
46 - 'content': { 'text': 'Obama\'s campaign theme was that he was a man of hope and change. He was also against the war in Iraq. He was in favor of giving money to American car companies. He was in favor of sending more troops to Afghanistan.' }
 116+ 'content': {
 117+ 'text': 'It is also possible to design a WIMP interface that intentionally does not make use of direct manipulation. For example, most versions of windowing interfaces (e.g. Microsoft Windows) allowed users to reposition a window by dragging it with the mouse, but would not continually redraw the complete window at intermediate positions during the drag. Instead, for example, a rectangular outline of the window might be drawn during the drag, with the complete window contents being redrawn only once the user had released the mouse button. This was necessary on older computers that lacked the memory and/or CPU power to quickly redraw data behind a window that was being dragged.',
 118+ 'annotations': [
 119+ {
 120+ 'type': 'link/internal',
 121+ 'data': {
 122+ 'title': 'Microsoft_Windows'
 123+ },
 124+ 'range': {
 125+ 'start': 165,
 126+ 'end': 182
 127+ }
 128+ }
 129+ ]
 130+ }
47131 },
48132 {
49 - 'type': 'paragraph',
50 - 'content': { 'text': 'During the campaign, some people said that Obama\'s friends included a Tony Rezko, a landlord, and former member of the Weather Underground, Bill Ayers but Obama said that they were not his friends. Obama also had trouble when his minister at church, Jeremiah Wright, was videotaped criticizing America. During the campaign, Obama said that his opponent, Republican candidate John McCain, was just like George W. Bush, something that John McCain said was not true.' }
51 - },
52 - {
53 - 'type': 'paragraph',
54 - 'content': { 'text': 'He defeated McCain in the election on November 4 by a wide electoral majority of 365 to 173, meaning that he won the most votes in enough states to send 365 people to officially elect him. The popular vote (based on the total number of votes across the country) was closer, with Obama winning 53%, McCain 46%.' }
55 - },
56 - {
57 - 'type': 'heading',
58 - 'attributes': { 'level': 2 },
59 - 'content': { 'text': 'Family' }
60 - },
61 - {
62 - 'type': 'paragraph',
63 - 'content': { 'text': 'Obama has been married to Michelle Obama since 1992. She has a Bachelor of Arts degree from Princeton University and also a Juris Doctor degree from Harvard Law School. She worked as a lawyer. They have two daughters, Malia Ann who was born in 1998 and Natasha ("Sasha"), born in 2001. They lived in Chicago, but moved into the White House on January 20, 2009.' }
64 - },
65 - {
66 - 'type': 'paragraph',
67 - 'content': { 'text': 'Obama promised his daughters that the family would get a dog if he was elected President. In April 2009, Senator Ted Kennedy, who once tried to run for President, gave Obama one of his dogs, a Portuguese water dog named Bo.' }
68 - },
69 - {
70 - 'type': 'paragraph',
71 - 'content': { 'text': 'Obama has a half sister who is a teacher in Hawaii. His father died from a car accident in Africa. His mother died of cancer. His grandmother died just before Obama won the election to become President.' }
72 - },
73 - {
74 - 'type': 'heading',
75 - 'attributes': { 'level': 2 },
76 - 'content': { 'text': 'Presidency' }
77 - },
78 - {
79 - 'type': 'paragraph',
80 - 'content': { 'text': 'Obama became President of the United States on January 20, 2009.' }
81 - },
82 - {
83 - 'type': 'paragraph',
84 - 'content': { 'text': 'When Obama became President, the United States was battling a tough recession. He asked Congress to spend an extra $787 billion ($787,000,000,000) to try to end the recession. He called the plan the stimulus bill. The stimulus bill funded many road projects, gave money to schools, gave tax credits to many Americans, and funded many science and research projects.' }
85 - },
86 - {
87 - 'type': 'paragraph',
88 - 'content': { 'text': 'Obama continued the financial bailout that George W. Bush started, giving billions of dollars to car companies and banks so that they will not go bankrupt. He signed an act written by Barney Frank and Chris Dodd which would regulate Wall Street (the financial industry) to try to prevent another recession like this from happening again.' }
89 - },
90 - {
91 - 'type': 'paragraph',
92 - 'content': { 'text': 'Obama signed the Patient Protection and Affordable Care Act which would bring health care reform to the United States, which he said would change the system so that more people can afford health care.' }
93 - },
94 - {
95 - 'type': 'paragraph',
96 - 'content': { 'text': 'In foreign policy, Obama made a plan to slowly withdraw troops from Iraq, ending the War In Iraq by the end of 2011, while adding more troops to Afghanistan to help the United States win the War In Afghanistan. He also decided that the USA should help in the war against Libya. He has said several times that he wants to improve U.S. relations with the Muslim world.' }
97 - },
98 - {
99 - 'type': 'paragraph',
100 - 'content': { 'text': 'Obama received the 2009 Nobel Peace Prize on October 9, 2009. He noted that his efforts were humble, but he donated the prize money to several charities.' }
101 - },
102 - {
103 - 'type': 'paragraph',
104 - 'content': { 'text': 'Although his popularity was very high (around 70% approval) when he entered office, his approval ratings fell to 45% percent during the year of 2010. He has received a lot of criticism from Republicans, conservatives, libertarians, and members of the Tea Party because they believe that, under the Obama Administration, the federal government is becoming too big and spending too much money and that his programs are not the best for the country.' }
105 - },
106 - {
107133 'type': 'pre',
108134 'content': { 'text': 'A lot of text goes here... and at some point it wraps.. A lot of text goes here... and at some point it wraps.. A lot of text goes here... and at some point it wraps.. A lot of text goes here... and at some point it wraps.. A lot of text goes here... and at some point it wraps..' }
109135 },
@@ -403,13 +429,13 @@
404430 for ( var key in tools ) {
405431 tools[key].removeClass( 'es-toolbarTool-down' );
406432 }
407 - if ( range.start == range.end ) {
408 - var annotations = doc.getAnnotationsFromOffset( range.start );
409 - if ( annotations.length ) {
410 - for ( var i = 0; i < annotations.length; i++ ) {
411 - if ( annotations[i].type in tools ) {
412 - tools[annotations[i].type].addClass( 'es-toolbarTool-down' );
413 - }
 433+ var annotations = range.getLength() ?
 434+ doc.getAnnotationsFromRange( range ) : doc.getAnnotationsFromOffset( range.start );
 435+ console.log( es.DocumentModel.getHash( annotations ) );
 436+ if ( annotations.length ) {
 437+ for ( var i = 0; i < annotations.length; i++ ) {
 438+ if ( annotations[i].type in tools ) {
 439+ tools[annotations[i].type].addClass( 'es-toolbarTool-down' );
414440 }
415441 }
416442 }
Index: trunk/extensions/VisualEditor/modules/es/models/es.DocumentModel.js
@@ -144,26 +144,25 @@
145145 /**
146146 * Generates a hash of an annotation object based on it's name and data.
147147 *
148 - * TODO: Add support for deep hashing of array and object properties of annotation data.
149 - *
150148 * @static
151149 * @method
152150 * @param {Object} annotation Annotation object to generate hash for
153151 * @returns {String} Hash of annotation
154152 */
155 -es.DocumentModel.getAnnotationHash = function( annotation ) {
156 - var hash = '#' + annotation.type;
157 - if ( annotation.data ) {
158 - var keys = [];
159 - for ( var key in annotation.data ) {
160 - keys.push( key + ':' + annotation.data );
161 - }
162 - keys.sort();
163 - hash += '|' + keys.join( '|' );
164 - }
165 - return hash;
166 -};
 153+es.DocumentModel.getHash = typeof JSON.stringify === 'function' ?
 154+ JSON.stringify : es.JsonSerializer.stringify;
167155
 156+/**
 157+ * Gets the index of the first instance of a given annotation.
 158+ *
 159+ * This method differs from es.inArray because it compares hashes instead of references.
 160+ *
 161+ * @static
 162+ * @method
 163+ * @param {Array} annotations Annotations to search through
 164+ * @param {Object} annotation Annotation to search for
 165+ * @returns {Integer} Index of annotation in annotations, or -1 if annotation was not found
 166+ */
168167 es.DocumentModel.getIndexOfAnnotation = function( annotations, annotation ) {
169168 if ( annotation === undefined || annotation.type === undefined ) {
170169 throw 'Invalid annotation error. Can not find non-annotation data in character.';
@@ -184,6 +183,83 @@
185184 };
186185
187186 /**
 187+ * Sorts annotations of a character.
 188+ *
 189+ * This method modifies data in place. The string portion of the annotation character will always
 190+ * remain at the beginning.
 191+ *
 192+ * @static
 193+ * @method
 194+ * @param {Array} character Annotated character to be sorted
 195+ */
 196+es.DocumentModel.sortCharacterAnnotations = function( character ) {
 197+ if ( !es.isArray( character ) ) {
 198+ return;
 199+ }
 200+ character.sort( function( a, b ) {
 201+ var aHash = a.hash || es.DocumentModel.getHash( a ),
 202+ bHash = b.hash || es.DocumentModel.getHash( b );
 203+ return typeof a === 'string' ? -1 :
 204+ ( typeof b === 'string' ? 1 : ( aHash == bHash ? 0 : ( aHash < bHash ? -1 : 1 ) ) );
 205+ } );
 206+};
 207+
 208+/**
 209+ * Adds annotation hashes to content data.
 210+ *
 211+ * This method modifies data in place.
 212+ *
 213+ * @method
 214+ * @param {Array} data Data to add annotation hashes to
 215+ */
 216+es.DocumentModel.addAnnotationHashesToData = function( data ) {
 217+ for ( var i = 0; i < data.length; i++ ) {
 218+ if ( es.isArray( data[i] ) ) {
 219+ for ( var j = 1; j < data.length; j++ ) {
 220+ if ( data[i][j].hash === undefined ) {
 221+ data[i][j].hash = es.DocumentModel.getHash( data[i][j] );
 222+ }
 223+ }
 224+ }
 225+ }
 226+};
 227+
 228+/**
 229+ * Applies annotations to content data.
 230+ *
 231+ * This method modifies data in place.
 232+ *
 233+ * @method
 234+ * @param {Array} data Data to remove annotations from
 235+ * @param {Array} annotations Annotations to apply
 236+ */
 237+es.DocumentModel.addAnnotationsToData = function( data, annotations ) {
 238+ for ( var i = 0; i < data.length; i++ ) {
 239+ if ( es.isArray( data[i] ) ) {
 240+ data[i] = [data[i]];
 241+ }
 242+ data[i] = [data[i]].concat( annotations );
 243+ }
 244+};
 245+
 246+/**
 247+ * Removes annotations from content data.
 248+ *
 249+ * This method modifies data in place.
 250+ *
 251+ * @method
 252+ * @param {Array} data Data to remove annotations from
 253+ * @param {Array} [annotations] Annotations to remove (all will be removed if undefined)
 254+ */
 255+es.DocumentModel.removeAnnotationsFromData = function( data, annotations ) {
 256+ for ( var i = 0; i < data.length; i++ ) {
 257+ if ( es.isArray( data[i] ) ) {
 258+ data[i] = data[i][0];
 259+ }
 260+ }
 261+};
 262+
 263+/**
188264 * Creates an es.ContentModel object from a plain content object.
189265 *
190266 * A plain content object contains plain text and a series of annotations to be applied to ranges of
@@ -228,7 +304,7 @@
229305 dst.data = es.copyObject( src.data );
230306 }
231307 // Add a hash to the annotation for faster comparison
232 - dst.hash = es.DocumentModel.getAnnotationHash( dst );
 308+ dst.hash = es.DocumentModel.getHash( dst );
233309 // Apply annotation to range
234310 if ( src.range.start < 0 ) {
235311 // TODO: The start can not be lower than 0! Throw error?
@@ -487,7 +563,8 @@
488564 start = Math.max( 0, Math.min( this.data.length, range.start ) );
489565 end = Math.max( 0, Math.min( this.data.length, range.end ) );
490566 }
491 - // Work around IE bug: arr.slice( 0, undefined ) returns [] while arr.slice( 0 ) behaves correctly
 567+ // Work around IE bug: arr.slice( 0, undefined ) returns [] while arr.slice( 0 ) behaves
 568+ // correctly
492569 var data = end === undefined ? this.data.slice( start ) : this.data.slice( start, end );
493570 return deep ? es.copyArray( data ) : data;
494571 };
@@ -548,7 +625,7 @@
549626 */
550627 es.DocumentModel.prototype.getAnnotationBoundaries = function( offset, annotation ) {
551628 if ( annotation.hash === undefined ) {
552 - annotation.hash = es.DocumentModel.getAnnotationHash( annotation );
 629+ annotation.hash = es.DocumentModel.getHash( annotation );
553630 }
554631 if ( es.DocumentModel.getIndexOfAnnotation( this.data[offset], annotation ) === -1 ) {
555632 return null;
@@ -587,6 +664,51 @@
588665 };
589666
590667 /**
 668+ * Gets a list of annotations that a given range is covered by.
 669+ *
 670+ * @method
 671+ * @param {es.Range} range Range to get annotations for
 672+ * @returns {Object[]} A copy of all annotation objects offset is covered by
 673+ */
 674+es.DocumentModel.prototype.getAnnotationsFromRange = function( range ) {
 675+ range.normalize();
 676+ // First pass - check that [0] and [n) characters are annotated
 677+ if ( !es.isArray( this.data[range.start] ) || !es.isArray( this.data[range.end - 1] ) ) {
 678+ // Range starts/ends on a non-annotated character, range can not have any common annotations
 679+ return [];
 680+ }
 681+ // Second pass - check that [1..n-1) characters are annotated
 682+ var i;
 683+ for ( i = range.start + 1, end = range.end - 1; i < end; i++ ) {
 684+ if ( !es.isArray( this.data[i] ) ) {
 685+ return [];
 686+ }
 687+ }
 688+ // Third pass - collect annotations common amung all characters
 689+ var map = {},
 690+ j,
 691+ hash;
 692+ for ( i = range.start, end = range.end; i < end; i++ ) {
 693+ for ( j = 1; j < this.data[i].length; j++ ) {
 694+ hash = this.data[i][j].hash;
 695+ if ( hash in map ) {
 696+ map[hash][1]++;
 697+ } else {
 698+ map[hash] = [this.data[i][j], 1];
 699+ }
 700+ }
 701+ }
 702+ var length = range.getLength(),
 703+ annotations = [];
 704+ for ( hash in map ) {
 705+ if ( map[hash][1] === length ) {
 706+ annotations.push( map[hash][0] );
 707+ }
 708+ }
 709+ return es.copyArray( annotations );
 710+};
 711+
 712+/**
591713 * Gets the range of content surrounding a given offset that makes up a whole word.
592714 *
593715 * @method
@@ -599,7 +721,8 @@
600722 return null;
601723 }
602724
603 - var offsetItem = typeof this.data[offset] === 'string' ? this.data[offset] : this.data[offset][0],
 725+ var offsetItem = typeof this.data[offset] === 'string' ?
 726+ this.data[offset] : this.data[offset][0],
604727 regex = offsetItem.match( /\B/ ) ? /\b/ : /\B/,
605728 start = offset,
606729 end = offset,
@@ -697,8 +820,8 @@
698821 // Closing doesn't match what's expected
699822 // This means the input is malformed and cannot possibly
700823 // have been a fragment taken from well-formed data
701 - throw 'Input is malformed: expected /' + element + ' but got ' + data[i].type +
702 - ' at index ' + i;
 824+ throw 'Input is malformed: expected /' + element + ' but got ' +
 825+ data[i].type + ' at index ' + i;
703826 }
704827 }
705828 }
@@ -728,7 +851,8 @@
729852 throw 'Offset ' + offset + ' out of bounds [0..' + this.data.length + ']';
730853 }
731854
732 - // Has to be after the bounds check, because isStructuralOffset doesn't like out-of-bounds offsets
 855+ // Has to be after the bounds check, because isStructuralOffset doesn't like out-of-bounds
 856+ // offsets
733857 isStructuralLoc = es.DocumentModel.isStructuralOffset( this.data, offset );
734858
735859 if ( offset > 0 ) {
@@ -745,7 +869,7 @@
746870 // We're inserting structure at a content location,
747871 // so we need to split up the wrapping element
748872 wrappingElementType = this.getNodeFromOffset( offset ).getElementType();
749 - var arr = [ { 'type': '/' + wrappingElementType }, { 'type': wrappingElementType } ];
 873+ var arr = [{ 'type': '/' + wrappingElementType }, { 'type': wrappingElementType }];
750874 es.insertIntoArray( arr, 1, insertedData );
751875 insertedData = arr;
752876 }
@@ -926,7 +1050,7 @@
9271051 var tx = new es.TransactionModel();
9281052 range.normalize();
9291053 if ( annotation.hash === undefined ) {
930 - annotation.hash = es.DocumentModel.getAnnotationHash( annotation );
 1054+ annotation.hash = es.DocumentModel.getHash( annotation );
9311055 }
9321056 var i = range.start,
9331057 span = i,
Index: trunk/extensions/VisualEditor/modules/es/es.Surface.css
@@ -1,5 +1,6 @@
22 .es-surfaceView {
33 overflow: hidden;
 4+ font-size: 1em; /* to look more like MediaWiki use: 0.8em */;
45 }
56
67 .es-surfaceView-textarea {
@@ -47,7 +48,6 @@
4849 margin-top: 0;
4950 position: relative;
5051 min-height: 1.5em;
51 - font-size: 1em;
5252 }
5353
5454 .es-listItemView > .es-paragraphView {
@@ -240,7 +240,8 @@
241241 .es-contentView-format-object a:link,
242242 .es-contentView-format-object a:visited,
243243 .es-contentView-format-object a:active {
244 - color: blue;
 244+ color: #0645AD;
 245+ text-decoration: none;
245246 }
246247
247248 .es-contentView-format-textStyle-italic,
@@ -254,7 +255,7 @@
255256 }
256257
257258 .es-contentView-format-link {
258 - color: blue;
 259+ color: #0645AD;
259260 text-decoration: underline;
260261 }
261262
Index: trunk/extensions/VisualEditor/modules/es/es.TransactionProcessor.js
@@ -182,7 +182,7 @@
183183 annotation = this.set[i];
184184 // Auto-build annotation hash
185185 if ( annotation.hash === undefined ) {
186 - annotation.hash = es.DocumentModel.getAnnotationHash( annotation );
 186+ annotation.hash = es.DocumentModel.getHash( annotation );
187187 }
188188 for ( j = this.cursor; j < to; j++ ) {
189189 // Auto-convert to array
@@ -199,7 +199,7 @@
200200 annotation = this.clear[i];
201201 // Auto-build annotation hash
202202 if ( annotation.hash === undefined ) {
203 - annotation.hash = es.DocumentModel.getAnnotationHash( annotation );
 203+ annotation.hash = es.DocumentModel.getHash( annotation );
204204 }
205205 for ( j = this.cursor; j < to; j++ ) {
206206 var index = es.DocumentModel.getIndexOfAnnotation( this.model.data[j], annotation );

Status & tagging log