Property changes on: trunk/parsers/wikidom/tests/wikidom/index.html |
___________________________________________________________________ |
Added: svn:eol-style |
1 | 1 | + native |
Property changes on: trunk/parsers/wikidom/tests/annotations/index.html |
___________________________________________________________________ |
Added: svn:eol-style |
2 | 2 | + native |
Property changes on: trunk/parsers/wikidom/lib/es/es.Surface.js |
___________________________________________________________________ |
Added: eol-style |
3 | 3 | + native |
Index: trunk/parsers/wikidom/lib/es/es.Content.js |
— | — | @@ -1,501 +1,501 @@ |
2 | | -/* Classes */
|
3 | | -
|
4 | | -/**
|
5 | | - * Content objects are wrappers around arrays of plain or annotated characters. Data in this form
|
6 | | - * is ultimately equivalent to but more efficient to work with than WikiDom line objects (plain text
|
7 | | - * paired with offset annotation), especially when performing substring operations. Content can be
|
8 | | - * derived from or converted to one or more WikiDom line objects.
|
9 | | - *
|
10 | | - * @param content {Array} List of plain or annotated characters
|
11 | | - * @returns {Content}
|
12 | | - */
|
13 | | -function Content( content ) {
|
14 | | - this.data = content || [];
|
15 | | -};
|
16 | | -
|
17 | | -/* Static Members */
|
18 | | -
|
19 | | -/**
|
20 | | - * List of annotation rendering implementations.
|
21 | | - *
|
22 | | - * Each supported annotation renderer must have an open and close property, each either a string or
|
23 | | - * a function which accepts a data argument.
|
24 | | - */
|
25 | | -Content.annotationRenderers = {
|
26 | | - 'template': {
|
27 | | - 'open': function( data ) {
|
28 | | - return '<span class="editSurface-format-object">' + data.html;
|
29 | | - },
|
30 | | - 'close': '</span>',
|
31 | | - },
|
32 | | - 'bold': {
|
33 | | - 'open': '<span class="editSurface-format-bold">',
|
34 | | - 'close': '</span>',
|
35 | | - },
|
36 | | - 'italic': {
|
37 | | - 'open': '<span class="editSurface-format-italic">',
|
38 | | - 'close': '</span>',
|
39 | | - },
|
40 | | - 'size': {
|
41 | | - 'open': function( data ) {
|
42 | | - return '<span class="editSurface-format-' + data.type + '">';
|
43 | | - },
|
44 | | - 'close': '</span>',
|
45 | | - },
|
46 | | - 'script': {
|
47 | | - 'open': function( data ) {
|
48 | | - return '<span class="editSurface-format-' + data.type + '">';
|
49 | | - },
|
50 | | - 'close': '</span>',
|
51 | | - },
|
52 | | - 'link': {
|
53 | | - 'open': function( data ) {
|
54 | | - return '<span class="editSurface-format-link" data-href="' + data.href + '">';
|
55 | | - },
|
56 | | - 'close': '</span>'
|
57 | | - }
|
58 | | -};
|
59 | | -
|
60 | | -Content.htmlCharacters = {
|
61 | | - '&': '&',
|
62 | | - '<': '<',
|
63 | | - '>': '>',
|
64 | | - '\'': ''',
|
65 | | - '"': '"',
|
66 | | - ' ': ' ',
|
67 | | - '\n': '<span class="editSurface-whitespace">¶</span>',
|
68 | | - '\t': '<span class="editSurface-whitespace">⇾</span>'
|
69 | | -};
|
70 | | -
|
71 | | -/* Static Methods */
|
72 | | -
|
73 | | -/**
|
74 | | - * Recursively compares string and number property between two objects.
|
75 | | - *
|
76 | | - * A false result may be caused by property inequality or by properties in one object missing from
|
77 | | - * the other. An asymmetrical test may also be performed, which checks only that properties in the
|
78 | | - * first object are present in the second object, but not the inverse.
|
79 | | - *
|
80 | | - * @param a {Object} First object to compare
|
81 | | - * @param b {Object} Second object to compare
|
82 | | - * @param asymmetrical {Boolean} Whether to check only that b contains values from a
|
83 | | - * @return {Boolean} If the objects contain the same values as each other
|
84 | | - */
|
85 | | -Content.compareObjects = function( a, b, asymmetrical ) {
|
86 | | - var aValue, bValue, aType, bType;
|
87 | | - for ( var k in a ) {
|
88 | | - aValue = a[k];
|
89 | | - bValue = b[k];
|
90 | | - aType = typeof aValue;
|
91 | | - bType = typeof bValue;
|
92 | | - if ( aType !== bType
|
93 | | - || ( ( aType === 'string' || aType === 'number' ) && aValue !== bValue )
|
94 | | - || ( $.isPlainObject( aValue ) && !Content.compareObjects( aValue, bValue ) ) ) {
|
95 | | - return false
|
96 | | - }
|
97 | | - }
|
98 | | - // If the check is not asymmetrical, recursing with the arguments swapped will verify our result
|
99 | | - return asymmetrical ? true : Content.compareObjects( b, a, true );
|
100 | | -};
|
101 | | -
|
102 | | -/**
|
103 | | - * Gets a recursive copy of an object's string, number and plain-object property.
|
104 | | - *
|
105 | | - * @param source {Object} Object to copy
|
106 | | - * @return {Object} Copy of source object
|
107 | | - */
|
108 | | -Content.copyObject = function( source ) {
|
109 | | - var destination = {};
|
110 | | - for ( var key in source ) {
|
111 | | - sourceValue = source[key];
|
112 | | - sourceType = typeof sourceValue;
|
113 | | - if ( sourceType === 'string' || sourceType === 'number' ) {
|
114 | | - destination[key] = sourceValue;
|
115 | | - } else if ( $.isPlainObject( sourceValue ) ) {
|
116 | | - destination[key] = Content.copyObject( sourceValue );
|
117 | | - }
|
118 | | - }
|
119 | | - return destination;
|
120 | | -};
|
121 | | -
|
122 | | -/**
|
123 | | - * Gets content data from a WikiDom line object, which uses a series of offset-based annotations to
|
124 | | - * supplement plain text.
|
125 | | - *
|
126 | | - * @param line {Object} WikiDom compatible line object, containing text and optionally annotations
|
127 | | - * properties, the latter of which being an array of annotation objects including range information
|
128 | | - * @return {Array} List of plain or annotated characters
|
129 | | - */
|
130 | | -Content.convertLine = function( line ) {
|
131 | | - // Convert string to array of characters
|
132 | | - var data = line.text.split('');
|
133 | | - for ( var i in line.annotations ) {
|
134 | | - var src = line.annotations[i];
|
135 | | - // Build simplified annotation object
|
136 | | - var dst = { 'type': src.type };
|
137 | | - if ( 'data' in src ) {
|
138 | | - dst.data = Content.copyObject( src.data );
|
139 | | - }
|
140 | | - // Apply annotation to range
|
141 | | - for ( var k = src.range.start; k < src.range.end; k++ ) {
|
142 | | - // Auto-convert to array
|
143 | | - typeof data[k] === 'string' && ( data[k] = [data[k]] );
|
144 | | - // Append
|
145 | | - data[k].push( dst );
|
146 | | - }
|
147 | | - }
|
148 | | - return data;
|
149 | | -};
|
150 | | -
|
151 | | -/**
|
152 | | - * Creates a new Content object from a WikiDom line object.
|
153 | | - *
|
154 | | - * @param line {Object} WikiDom compatible line object - @see Content.convertLine
|
155 | | - * @return {Content} New content object containing data derived from the WikiDom line
|
156 | | - */
|
157 | | -Content.newFromLine = function( line ) {
|
158 | | - return new Content( Content.convertLine( line ) );
|
159 | | -};
|
160 | | -
|
161 | | -/**
|
162 | | - * Creates a new Content object from a list of WikiDom line objects.
|
163 | | - *
|
164 | | - * This plural version of Content.newFromLine inserts non-annotated new line characters between
|
165 | | - * lines, preserving the divisions between the original line objects. When Content objects are
|
166 | | - * converted to WikiDom line objects, these new line characters are used to split the content data
|
167 | | - * into multiple line objects, thus making a clean round trip possible.
|
168 | | - *
|
169 | | - * @param line {Array} List of WikiDom compatible line objects - @see Content.convertLine
|
170 | | - * @return {Content} New content object containing data derived from the WikiDom line
|
171 | | - */
|
172 | | -Content.newFromLines = function( lines ) {
|
173 | | - var data = [];
|
174 | | - for ( var i = 0; i < lines.length; i++ ) {
|
175 | | - data = data.concat( Content.convertLine( lines[i] ) );
|
176 | | - if ( i < lines.length - 1 ) {
|
177 | | - data.push( '\n' );
|
178 | | - }
|
179 | | - }
|
180 | | - return new Content( data );
|
181 | | -};
|
182 | | -
|
183 | | -/**
|
184 | | - * Gets a rendered opening or closing of an annotation.
|
185 | | - *
|
186 | | - * Tag nesting is handled using a stack, which keeps track of what is currently open. A common stack
|
187 | | - * argument should be used while rendering content.
|
188 | | - *
|
189 | | - * @param bias {String} Which side of the annotation to render, either "open" or "close"
|
190 | | - * @param annotation {Object} Annotation to render
|
191 | | - * @param stack {Array} List of currently open annotations
|
192 | | - * @return {String} Rendered annotation
|
193 | | - */
|
194 | | -Content.renderAnnotation = function( bias, annotation, stack ) {
|
195 | | - var renderers = Content.annotationRenderers,
|
196 | | - type = annotation.type,
|
197 | | - out = '';
|
198 | | - if ( type in renderers ) {
|
199 | | - if ( bias === 'open' ) {
|
200 | | - // Add annotation to the top of the stack
|
201 | | - stack.push( annotation );
|
202 | | - // Open annotation
|
203 | | - out += typeof renderers[type]['open'] === 'function'
|
204 | | - ? renderers[type]['open']( annotation.data )
|
205 | | - : renderers[type]['open'];
|
206 | | - } else {
|
207 | | - if ( stack[stack.length - 1] === annotation ) {
|
208 | | - // Remove annotation from top of the stack
|
209 | | - stack.pop();
|
210 | | - // Close annotation
|
211 | | - out += typeof renderers[type]['close'] === 'function'
|
212 | | - ? renderers[type]['close']( annotation.data )
|
213 | | - : renderers[type]['close'];
|
214 | | - } else {
|
215 | | - // Find the annotation in the stack
|
216 | | - var depth = stack.indexOf( annotation );
|
217 | | - if ( depth === -1 ) {
|
218 | | - throw 'Invalid stack error. An element is missing from the stack.';
|
219 | | - }
|
220 | | - // Close each already opened annotation
|
221 | | - for ( var i = stack.length - 1; i >= depth + 1; i-- ) {
|
222 | | - out += typeof renderers[stack[i].type]['close'] === 'function'
|
223 | | - ? renderers[stack[i].type]['close']( stack[i].data )
|
224 | | - : renderers[stack[i].type]['close'];
|
225 | | - }
|
226 | | - // Close the buried annotation
|
227 | | - out += typeof renderers[type]['close'] === 'function'
|
228 | | - ? renderers[type]['close']( annotation.data )
|
229 | | - : renderers[type]['close'];
|
230 | | - // Re-open each previously opened annotation
|
231 | | - for ( var i = depth + 1; i < stack.length; i++ ) {
|
232 | | - out += typeof renderers[stack[i].type]['open'] === 'function'
|
233 | | - ? renderers[stack[i].type]['open']( stack[i].data )
|
234 | | - : renderers[stack[i].type]['open'];
|
235 | | - }
|
236 | | - // Remove the annotation from the middle of the stack
|
237 | | - stack.splice( depth, 1 );
|
238 | | - }
|
239 | | - }
|
240 | | - }
|
241 | | - return out;
|
242 | | -};
|
243 | | -
|
244 | | -/* Methods */
|
245 | | -
|
246 | | -/**
|
247 | | - * Gets plain text version of the content within a specific range.
|
248 | | - *
|
249 | | - * Range arguments (start and end) are clamped if out of range.
|
250 | | - *
|
251 | | - * @param start {Integer} Optional beginning of range, if omitted range will begin at 0
|
252 | | - * @param end {Integer} Optional end of range, if omitted range will end a this.data.length
|
253 | | - * @return {String} Plain text within given range
|
254 | | - */
|
255 | | -Content.prototype.substring = function( start, end ) {
|
256 | | - // Wrap values
|
257 | | - start = Math.max( 0, start || 0 );
|
258 | | - if ( end === undefined ) {
|
259 | | - end = this.data.length;
|
260 | | - } else {
|
261 | | - end = Math.min( this.data.length, end )
|
262 | | - }
|
263 | | - // Copy characters
|
264 | | - var text = '';
|
265 | | - for ( var i = start; i < end; i++ ) {
|
266 | | - // If not using in IE6 or IE7 (which do not support array access for strings) use this..
|
267 | | - // text += this.data[i][0];
|
268 | | - // Otherwise use this...
|
269 | | - text += typeof this.data[i] === 'string' ? this.data[i] : this.data[i][0];
|
270 | | - }
|
271 | | - return text;
|
272 | | -};
|
273 | | -
|
274 | | -/**
|
275 | | - * Gets a new Content object containing content data within a specific range.
|
276 | | - *
|
277 | | - * Range arguments (start and end) are clamped if out of range.
|
278 | | - *
|
279 | | - * @param start {Integer} Optional beginning of range, if omitted range will begin at 0
|
280 | | - * @param end {Integer} Optional end of range, if omitted range will end a this.data.length
|
281 | | - * @return {Content} New content object
|
282 | | - */
|
283 | | -Content.prototype.slice = function( start, end ) {
|
284 | | - return new Content( this.data.slice( start, end ) );
|
285 | | -};
|
286 | | -
|
287 | | -/**
|
288 | | - * Inserts content data at a specific position.
|
289 | | - *
|
290 | | - * Inserted content will inherit annotations from neighboring content.
|
291 | | - *
|
292 | | - * @param start {Integer} Position to insert content at
|
293 | | - * @param insert {Array} Content data to insert
|
294 | | - */
|
295 | | -Content.prototype.insert = function( start, insert ) {
|
296 | | - // TODO: Prefer to not take annotations from a neighbor that's a space character
|
297 | | - var neighbor = this.data[Math.max( start - 1, 0 )];
|
298 | | - if ( $.isArray( neighbor ) ) {
|
299 | | - var annotations = neighbor.slice( 1 );
|
300 | | - for ( var i = 0; i < insert.length; i++ ) {
|
301 | | - if ( typeof insert[i] === 'string' ) {
|
302 | | - insert[i] = [insert[i]];
|
303 | | - }
|
304 | | - insert[i] = insert[i].concat( annotations );
|
305 | | - }
|
306 | | - }
|
307 | | - Array.prototype.splice.apply( this.data, [start, 0].concat( insert ) )
|
308 | | -};
|
309 | | -
|
310 | | -/**
|
311 | | - * Removes content data within a specific range.
|
312 | | - *
|
313 | | - * @param start {Integer} Beginning of range
|
314 | | - * @param end {Integer} End of range
|
315 | | - */
|
316 | | -Content.prototype.remove = function( start, end ) {
|
317 | | - this.data.splice( start, end - start );
|
318 | | -};
|
319 | | -
|
320 | | -/**
|
321 | | - * Gets the length of the content data.
|
322 | | - *
|
323 | | - * @return {Integer} Length of content data
|
324 | | - */
|
325 | | -Content.prototype.getLength = function() {
|
326 | | - return this.data.length;
|
327 | | -};
|
328 | | -
|
329 | | -/**
|
330 | | - * Gets a list of indexes within the content data which use a given annotation.
|
331 | | - *
|
332 | | - * Strict coverage may be used to compare not only annotation types, but also their data. Since new
|
333 | | - * line characters are never annotated, they are always considered covered.
|
334 | | - *
|
335 | | - * @param start {Integer} Beginning of range
|
336 | | - * @param end {Integer} End of range
|
337 | | - * @param annotation {Object} Annotation to compare with
|
338 | | - * @param strict {Boolean} Optionally compare annotation data as well as type
|
339 | | - * @return {Array} List of indexes of covered characters within content data
|
340 | | - */
|
341 | | -Content.prototype.coverageOfAnnotation = function( start, end, annotation, strict ) {
|
342 | | - var coverage = [];
|
343 | | - for ( var i = start; i < end; i++ ) {
|
344 | | - var index = this.indexOfAnnotation( i, annotation );
|
345 | | - if ( typeof this.data[i] !== 'string' && index !== -1 ) {
|
346 | | - if ( strict ) {
|
347 | | - if ( Content.compareObjects( this.data[i][index].data, annotation.data ) ) {
|
348 | | - coverage.push( i );
|
349 | | - }
|
350 | | - } else {
|
351 | | - coverage.push( i );
|
352 | | - }
|
353 | | - } else if ( this.data[i] === '\n' ) {
|
354 | | - coverage.push( i );
|
355 | | - }
|
356 | | - }
|
357 | | - return coverage;
|
358 | | -};
|
359 | | -
|
360 | | -/**
|
361 | | - * Gets the first index within an annotated character that matches a given annotation.
|
362 | | - *
|
363 | | - * Strict coverage may be used to compare not only annotation types, but also their data.
|
364 | | - *
|
365 | | - * @param offset {Integer} Index of character within content data to find annotation within
|
366 | | - * @param annotation {Object} Annotation to compare with
|
367 | | - * @param strict {Boolean} Optionally compare annotation data as well as type
|
368 | | - */
|
369 | | -Content.prototype.indexOfAnnotation = function( offset, annotation, strict ) {
|
370 | | - var annotatedCharacter = this.data[offset];
|
371 | | - if ( typeof annotatedCharacter !== 'string' ) {
|
372 | | - for ( var i = 1; i < this.data[offset].length; i++ ) {
|
373 | | - if ( annotatedCharacter[i].type === annotation.type ) {
|
374 | | - if ( strict ) {
|
375 | | - if ( Content.compareObjects( annotatedCharacter[i].data, annotation.data ) ) {
|
376 | | - return i;
|
377 | | - }
|
378 | | - } else {
|
379 | | - return i;
|
380 | | - }
|
381 | | - }
|
382 | | - }
|
383 | | - }
|
384 | | - return -1;
|
385 | | -};
|
386 | | -
|
387 | | -/**
|
388 | | - * Applies an annotation to content data within a given range.
|
389 | | - *
|
390 | | - * If a range arguments are not provided, all content will be annotated. New line characters are
|
391 | | - * never annotated. The add method will replace and the remove method will delete any existing
|
392 | | - * annotations with the same type as the annotation argument, regardless of their data properties.
|
393 | | - * The toggle method will use add if any of the content within the range is not already covered by
|
394 | | - * the annotation, or remove if all of it is.
|
395 | | - *
|
396 | | - * @param method {String} Way to apply annotation ("toggle", "add" or "remove")
|
397 | | - * @param annotation {Object} Annotation to apply
|
398 | | - * @param start {Integer} Offset to begin annotating from
|
399 | | - * @param end {Integer} Offset to stop annotating to
|
400 | | - */
|
401 | | -Content.prototype.annotate = function( method, annotation, start, end ) {
|
402 | | - start = Math.max( start, 0 );
|
403 | | - end = Math.min( end, this.data.length );
|
404 | | - if ( method === 'toggle' ) {
|
405 | | - var coverage = this.coverageOfAnnotation( start, end, annotation, false );
|
406 | | - if ( coverage.length === end - start ) {
|
407 | | - var strictCoverage = this.coverageOfAnnotation( start, end, annotation, true );
|
408 | | - method = strictCoverage.length === coverage.length ? 'remove' : 'add';
|
409 | | - } else {
|
410 | | - method = 'add';
|
411 | | - }
|
412 | | - }
|
413 | | - if ( method === 'add' ) {
|
414 | | - var duplicate;
|
415 | | - for ( var i = start; i < end; i++ ) {
|
416 | | - duplicate = -1;
|
417 | | - if ( typeof this.data[i] === 'string' ) {
|
418 | | - // Never annotate new lines
|
419 | | - if ( this.data[i] === '\n' ) {
|
420 | | - continue;
|
421 | | - }
|
422 | | - // Auto-initialize as annotated character
|
423 | | - this.data[i] = [this.data[i]];
|
424 | | - } else {
|
425 | | - // Detect duplicate annotation
|
426 | | - duplicate = this.indexOfAnnotation( i, annotation );
|
427 | | - }
|
428 | | - if ( duplicate === -1 ) {
|
429 | | - // Append new annotation
|
430 | | - this.data[i].push( annotation );
|
431 | | - } else {
|
432 | | - // Replace existing annotation
|
433 | | - this.data[i][duplicate] = annotation;
|
434 | | - }
|
435 | | - }
|
436 | | - } else if ( method === 'remove' ) {
|
437 | | - for ( var i = start; i < end; i++ ) {
|
438 | | - if ( typeof this.data[i] !== 'string' ) {
|
439 | | - if ( annotation.type === 'all' ) {
|
440 | | - // Remove all annotations by converting the annotated character to a plain
|
441 | | - // character
|
442 | | - this.data[i] = this.data[i][0];
|
443 | | - }
|
444 | | - // Remove all matching instances of annotation
|
445 | | - var j;
|
446 | | - while ( ( j = this.indexOfAnnotation( i, annotation ) ) !== -1 ) {
|
447 | | - this.data[i].splice( j, 1 );
|
448 | | - }
|
449 | | - }
|
450 | | - }
|
451 | | - }
|
452 | | -};
|
453 | | -
|
454 | | -/**
|
455 | | - * Gets an HTML rendering of a range of content data.
|
456 | | - *
|
457 | | - * @param start {Integer} Beginning of range
|
458 | | - * @param end {Integer} End of range
|
459 | | - * @param {String} Rendered HTML of content data
|
460 | | - */
|
461 | | -Content.prototype.render = function( start, end ) {
|
462 | | - if ( start || end ) {
|
463 | | - return this.slice( start, end ).render();
|
464 | | - }
|
465 | | - var out = '',
|
466 | | - left = '',
|
467 | | - right,
|
468 | | - leftPlain,
|
469 | | - rightPlain,
|
470 | | - stack = [];
|
471 | | - for ( var i = 0; i < this.data.length; i++ ) {
|
472 | | - right = this.data[i];
|
473 | | - leftPlain = typeof left === 'string';
|
474 | | - rightPlain = typeof right === 'string';
|
475 | | - if ( !leftPlain && rightPlain ) {
|
476 | | - // [formatted][plain] pair, close any annotations for left
|
477 | | - for ( var j = 1; j < left.length; j++ ) {
|
478 | | - out += Content.renderAnnotation( 'close', left[j], stack );
|
479 | | - }
|
480 | | - } else if ( leftPlain && !rightPlain ) {
|
481 | | - // [plain][formatted] pair, open any annotations for right
|
482 | | - for ( var j = 1; j < right.length; j++ ) {
|
483 | | - out += Content.renderAnnotation( 'open', right[j], stack );
|
484 | | - }
|
485 | | - } else if ( !leftPlain && !rightPlain ) {
|
486 | | - // [formatted][formatted] pair, open/close any differences
|
487 | | - for ( var j = 1; j < left.length; j++ ) {
|
488 | | - if ( right.indexOf( left[j] ) === -1 ) {
|
489 | | - out += Content.renderAnnotation( 'close', left[j], stack );
|
490 | | - }
|
491 | | - }
|
492 | | - for ( var j = 1; j < right.length; j++ ) {
|
493 | | - if ( left.indexOf( right[j] ) === -1 ) {
|
494 | | - out += Content.renderAnnotation( 'open', right[j], stack );
|
495 | | - }
|
496 | | - }
|
497 | | - }
|
498 | | - out += right[0] in Content.htmlCharacters ? Content.htmlCharacters[right[0]] : right[0];
|
499 | | - left = right;
|
500 | | - }
|
501 | | - return out;
|
502 | | -}
|
| 2 | +/* Classes */ |
| 3 | + |
| 4 | +/** |
| 5 | + * Content objects are wrappers around arrays of plain or annotated characters. Data in this form |
| 6 | + * is ultimately equivalent to but more efficient to work with than WikiDom line objects (plain text |
| 7 | + * paired with offset annotation), especially when performing substring operations. Content can be |
| 8 | + * derived from or converted to one or more WikiDom line objects. |
| 9 | + * |
| 10 | + * @param content {Array} List of plain or annotated characters |
| 11 | + * @returns {Content} |
| 12 | + */ |
| 13 | +function Content( content ) { |
| 14 | + this.data = content || []; |
| 15 | +}; |
| 16 | + |
| 17 | +/* Static Members */ |
| 18 | + |
| 19 | +/** |
| 20 | + * List of annotation rendering implementations. |
| 21 | + * |
| 22 | + * Each supported annotation renderer must have an open and close property, each either a string or |
| 23 | + * a function which accepts a data argument. |
| 24 | + */ |
| 25 | +Content.annotationRenderers = { |
| 26 | + 'template': { |
| 27 | + 'open': function( data ) { |
| 28 | + return '<span class="editSurface-format-object">' + data.html; |
| 29 | + }, |
| 30 | + 'close': '</span>', |
| 31 | + }, |
| 32 | + 'bold': { |
| 33 | + 'open': '<span class="editSurface-format-bold">', |
| 34 | + 'close': '</span>', |
| 35 | + }, |
| 36 | + 'italic': { |
| 37 | + 'open': '<span class="editSurface-format-italic">', |
| 38 | + 'close': '</span>', |
| 39 | + }, |
| 40 | + 'size': { |
| 41 | + 'open': function( data ) { |
| 42 | + return '<span class="editSurface-format-' + data.type + '">'; |
| 43 | + }, |
| 44 | + 'close': '</span>', |
| 45 | + }, |
| 46 | + 'script': { |
| 47 | + 'open': function( data ) { |
| 48 | + return '<span class="editSurface-format-' + data.type + '">'; |
| 49 | + }, |
| 50 | + 'close': '</span>', |
| 51 | + }, |
| 52 | + 'link': { |
| 53 | + 'open': function( data ) { |
| 54 | + return '<span class="editSurface-format-link" data-href="' + data.href + '">'; |
| 55 | + }, |
| 56 | + 'close': '</span>' |
| 57 | + } |
| 58 | +}; |
| 59 | + |
| 60 | +Content.htmlCharacters = { |
| 61 | + '&': '&', |
| 62 | + '<': '<', |
| 63 | + '>': '>', |
| 64 | + '\'': ''', |
| 65 | + '"': '"', |
| 66 | + ' ': ' ', |
| 67 | + '\n': '<span class="editSurface-whitespace">¶</span>', |
| 68 | + '\t': '<span class="editSurface-whitespace">⇾</span>' |
| 69 | +}; |
| 70 | + |
| 71 | +/* Static Methods */ |
| 72 | + |
| 73 | +/** |
| 74 | + * Recursively compares string and number property between two objects. |
| 75 | + * |
| 76 | + * A false result may be caused by property inequality or by properties in one object missing from |
| 77 | + * the other. An asymmetrical test may also be performed, which checks only that properties in the |
| 78 | + * first object are present in the second object, but not the inverse. |
| 79 | + * |
| 80 | + * @param a {Object} First object to compare |
| 81 | + * @param b {Object} Second object to compare |
| 82 | + * @param asymmetrical {Boolean} Whether to check only that b contains values from a |
| 83 | + * @return {Boolean} If the objects contain the same values as each other |
| 84 | + */ |
| 85 | +Content.compareObjects = function( a, b, asymmetrical ) { |
| 86 | + var aValue, bValue, aType, bType; |
| 87 | + for ( var k in a ) { |
| 88 | + aValue = a[k]; |
| 89 | + bValue = b[k]; |
| 90 | + aType = typeof aValue; |
| 91 | + bType = typeof bValue; |
| 92 | + if ( aType !== bType |
| 93 | + || ( ( aType === 'string' || aType === 'number' ) && aValue !== bValue ) |
| 94 | + || ( $.isPlainObject( aValue ) && !Content.compareObjects( aValue, bValue ) ) ) { |
| 95 | + return false |
| 96 | + } |
| 97 | + } |
| 98 | + // If the check is not asymmetrical, recursing with the arguments swapped will verify our result |
| 99 | + return asymmetrical ? true : Content.compareObjects( b, a, true ); |
| 100 | +}; |
| 101 | + |
| 102 | +/** |
| 103 | + * Gets a recursive copy of an object's string, number and plain-object property. |
| 104 | + * |
| 105 | + * @param source {Object} Object to copy |
| 106 | + * @return {Object} Copy of source object |
| 107 | + */ |
| 108 | +Content.copyObject = function( source ) { |
| 109 | + var destination = {}; |
| 110 | + for ( var key in source ) { |
| 111 | + sourceValue = source[key]; |
| 112 | + sourceType = typeof sourceValue; |
| 113 | + if ( sourceType === 'string' || sourceType === 'number' ) { |
| 114 | + destination[key] = sourceValue; |
| 115 | + } else if ( $.isPlainObject( sourceValue ) ) { |
| 116 | + destination[key] = Content.copyObject( sourceValue ); |
| 117 | + } |
| 118 | + } |
| 119 | + return destination; |
| 120 | +}; |
| 121 | + |
| 122 | +/** |
| 123 | + * Gets content data from a WikiDom line object, which uses a series of offset-based annotations to |
| 124 | + * supplement plain text. |
| 125 | + * |
| 126 | + * @param line {Object} WikiDom compatible line object, containing text and optionally annotations |
| 127 | + * properties, the latter of which being an array of annotation objects including range information |
| 128 | + * @return {Array} List of plain or annotated characters |
| 129 | + */ |
| 130 | +Content.convertLine = function( line ) { |
| 131 | + // Convert string to array of characters |
| 132 | + var data = line.text.split(''); |
| 133 | + for ( var i in line.annotations ) { |
| 134 | + var src = line.annotations[i]; |
| 135 | + // Build simplified annotation object |
| 136 | + var dst = { 'type': src.type }; |
| 137 | + if ( 'data' in src ) { |
| 138 | + dst.data = Content.copyObject( src.data ); |
| 139 | + } |
| 140 | + // Apply annotation to range |
| 141 | + for ( var k = src.range.start; k < src.range.end; k++ ) { |
| 142 | + // Auto-convert to array |
| 143 | + typeof data[k] === 'string' && ( data[k] = [data[k]] ); |
| 144 | + // Append |
| 145 | + data[k].push( dst ); |
| 146 | + } |
| 147 | + } |
| 148 | + return data; |
| 149 | +}; |
| 150 | + |
| 151 | +/** |
| 152 | + * Creates a new Content object from a WikiDom line object. |
| 153 | + * |
| 154 | + * @param line {Object} WikiDom compatible line object - @see Content.convertLine |
| 155 | + * @return {Content} New content object containing data derived from the WikiDom line |
| 156 | + */ |
| 157 | +Content.newFromLine = function( line ) { |
| 158 | + return new Content( Content.convertLine( line ) ); |
| 159 | +}; |
| 160 | + |
| 161 | +/** |
| 162 | + * Creates a new Content object from a list of WikiDom line objects. |
| 163 | + * |
| 164 | + * This plural version of Content.newFromLine inserts non-annotated new line characters between |
| 165 | + * lines, preserving the divisions between the original line objects. When Content objects are |
| 166 | + * converted to WikiDom line objects, these new line characters are used to split the content data |
| 167 | + * into multiple line objects, thus making a clean round trip possible. |
| 168 | + * |
| 169 | + * @param line {Array} List of WikiDom compatible line objects - @see Content.convertLine |
| 170 | + * @return {Content} New content object containing data derived from the WikiDom line |
| 171 | + */ |
| 172 | +Content.newFromLines = function( lines ) { |
| 173 | + var data = []; |
| 174 | + for ( var i = 0; i < lines.length; i++ ) { |
| 175 | + data = data.concat( Content.convertLine( lines[i] ) ); |
| 176 | + if ( i < lines.length - 1 ) { |
| 177 | + data.push( '\n' ); |
| 178 | + } |
| 179 | + } |
| 180 | + return new Content( data ); |
| 181 | +}; |
| 182 | + |
| 183 | +/** |
| 184 | + * Gets a rendered opening or closing of an annotation. |
| 185 | + * |
| 186 | + * Tag nesting is handled using a stack, which keeps track of what is currently open. A common stack |
| 187 | + * argument should be used while rendering content. |
| 188 | + * |
| 189 | + * @param bias {String} Which side of the annotation to render, either "open" or "close" |
| 190 | + * @param annotation {Object} Annotation to render |
| 191 | + * @param stack {Array} List of currently open annotations |
| 192 | + * @return {String} Rendered annotation |
| 193 | + */ |
| 194 | +Content.renderAnnotation = function( bias, annotation, stack ) { |
| 195 | + var renderers = Content.annotationRenderers, |
| 196 | + type = annotation.type, |
| 197 | + out = ''; |
| 198 | + if ( type in renderers ) { |
| 199 | + if ( bias === 'open' ) { |
| 200 | + // Add annotation to the top of the stack |
| 201 | + stack.push( annotation ); |
| 202 | + // Open annotation |
| 203 | + out += typeof renderers[type]['open'] === 'function' |
| 204 | + ? renderers[type]['open']( annotation.data ) |
| 205 | + : renderers[type]['open']; |
| 206 | + } else { |
| 207 | + if ( stack[stack.length - 1] === annotation ) { |
| 208 | + // Remove annotation from top of the stack |
| 209 | + stack.pop(); |
| 210 | + // Close annotation |
| 211 | + out += typeof renderers[type]['close'] === 'function' |
| 212 | + ? renderers[type]['close']( annotation.data ) |
| 213 | + : renderers[type]['close']; |
| 214 | + } else { |
| 215 | + // Find the annotation in the stack |
| 216 | + var depth = stack.indexOf( annotation ); |
| 217 | + if ( depth === -1 ) { |
| 218 | + throw 'Invalid stack error. An element is missing from the stack.'; |
| 219 | + } |
| 220 | + // Close each already opened annotation |
| 221 | + for ( var i = stack.length - 1; i >= depth + 1; i-- ) { |
| 222 | + out += typeof renderers[stack[i].type]['close'] === 'function' |
| 223 | + ? renderers[stack[i].type]['close']( stack[i].data ) |
| 224 | + : renderers[stack[i].type]['close']; |
| 225 | + } |
| 226 | + // Close the buried annotation |
| 227 | + out += typeof renderers[type]['close'] === 'function' |
| 228 | + ? renderers[type]['close']( annotation.data ) |
| 229 | + : renderers[type]['close']; |
| 230 | + // Re-open each previously opened annotation |
| 231 | + for ( var i = depth + 1; i < stack.length; i++ ) { |
| 232 | + out += typeof renderers[stack[i].type]['open'] === 'function' |
| 233 | + ? renderers[stack[i].type]['open']( stack[i].data ) |
| 234 | + : renderers[stack[i].type]['open']; |
| 235 | + } |
| 236 | + // Remove the annotation from the middle of the stack |
| 237 | + stack.splice( depth, 1 ); |
| 238 | + } |
| 239 | + } |
| 240 | + } |
| 241 | + return out; |
| 242 | +}; |
| 243 | + |
| 244 | +/* Methods */ |
| 245 | + |
| 246 | +/** |
| 247 | + * Gets plain text version of the content within a specific range. |
| 248 | + * |
| 249 | + * Range arguments (start and end) are clamped if out of range. |
| 250 | + * |
| 251 | + * @param start {Integer} Optional beginning of range, if omitted range will begin at 0 |
| 252 | + * @param end {Integer} Optional end of range, if omitted range will end a this.data.length |
| 253 | + * @return {String} Plain text within given range |
| 254 | + */ |
| 255 | +Content.prototype.substring = function( start, end ) { |
| 256 | + // Wrap values |
| 257 | + start = Math.max( 0, start || 0 ); |
| 258 | + if ( end === undefined ) { |
| 259 | + end = this.data.length; |
| 260 | + } else { |
| 261 | + end = Math.min( this.data.length, end ) |
| 262 | + } |
| 263 | + // Copy characters |
| 264 | + var text = ''; |
| 265 | + for ( var i = start; i < end; i++ ) { |
| 266 | + // If not using in IE6 or IE7 (which do not support array access for strings) use this.. |
| 267 | + // text += this.data[i][0]; |
| 268 | + // Otherwise use this... |
| 269 | + text += typeof this.data[i] === 'string' ? this.data[i] : this.data[i][0]; |
| 270 | + } |
| 271 | + return text; |
| 272 | +}; |
| 273 | + |
| 274 | +/** |
| 275 | + * Gets a new Content object containing content data within a specific range. |
| 276 | + * |
| 277 | + * Range arguments (start and end) are clamped if out of range. |
| 278 | + * |
| 279 | + * @param start {Integer} Optional beginning of range, if omitted range will begin at 0 |
| 280 | + * @param end {Integer} Optional end of range, if omitted range will end a this.data.length |
| 281 | + * @return {Content} New content object |
| 282 | + */ |
| 283 | +Content.prototype.slice = function( start, end ) { |
| 284 | + return new Content( this.data.slice( start, end ) ); |
| 285 | +}; |
| 286 | + |
| 287 | +/** |
| 288 | + * Inserts content data at a specific position. |
| 289 | + * |
| 290 | + * Inserted content will inherit annotations from neighboring content. |
| 291 | + * |
| 292 | + * @param start {Integer} Position to insert content at |
| 293 | + * @param insert {Array} Content data to insert |
| 294 | + */ |
| 295 | +Content.prototype.insert = function( start, insert ) { |
| 296 | + // TODO: Prefer to not take annotations from a neighbor that's a space character |
| 297 | + var neighbor = this.data[Math.max( start - 1, 0 )]; |
| 298 | + if ( $.isArray( neighbor ) ) { |
| 299 | + var annotations = neighbor.slice( 1 ); |
| 300 | + for ( var i = 0; i < insert.length; i++ ) { |
| 301 | + if ( typeof insert[i] === 'string' ) { |
| 302 | + insert[i] = [insert[i]]; |
| 303 | + } |
| 304 | + insert[i] = insert[i].concat( annotations ); |
| 305 | + } |
| 306 | + } |
| 307 | + Array.prototype.splice.apply( this.data, [start, 0].concat( insert ) ) |
| 308 | +}; |
| 309 | + |
| 310 | +/** |
| 311 | + * Removes content data within a specific range. |
| 312 | + * |
| 313 | + * @param start {Integer} Beginning of range |
| 314 | + * @param end {Integer} End of range |
| 315 | + */ |
| 316 | +Content.prototype.remove = function( start, end ) { |
| 317 | + this.data.splice( start, end - start ); |
| 318 | +}; |
| 319 | + |
| 320 | +/** |
| 321 | + * Gets the length of the content data. |
| 322 | + * |
| 323 | + * @return {Integer} Length of content data |
| 324 | + */ |
| 325 | +Content.prototype.getLength = function() { |
| 326 | + return this.data.length; |
| 327 | +}; |
| 328 | + |
| 329 | +/** |
| 330 | + * Gets a list of indexes within the content data which use a given annotation. |
| 331 | + * |
| 332 | + * Strict coverage may be used to compare not only annotation types, but also their data. Since new |
| 333 | + * line characters are never annotated, they are always considered covered. |
| 334 | + * |
| 335 | + * @param start {Integer} Beginning of range |
| 336 | + * @param end {Integer} End of range |
| 337 | + * @param annotation {Object} Annotation to compare with |
| 338 | + * @param strict {Boolean} Optionally compare annotation data as well as type |
| 339 | + * @return {Array} List of indexes of covered characters within content data |
| 340 | + */ |
| 341 | +Content.prototype.coverageOfAnnotation = function( start, end, annotation, strict ) { |
| 342 | + var coverage = []; |
| 343 | + for ( var i = start; i < end; i++ ) { |
| 344 | + var index = this.indexOfAnnotation( i, annotation ); |
| 345 | + if ( typeof this.data[i] !== 'string' && index !== -1 ) { |
| 346 | + if ( strict ) { |
| 347 | + if ( Content.compareObjects( this.data[i][index].data, annotation.data ) ) { |
| 348 | + coverage.push( i ); |
| 349 | + } |
| 350 | + } else { |
| 351 | + coverage.push( i ); |
| 352 | + } |
| 353 | + } else if ( this.data[i] === '\n' ) { |
| 354 | + coverage.push( i ); |
| 355 | + } |
| 356 | + } |
| 357 | + return coverage; |
| 358 | +}; |
| 359 | + |
| 360 | +/** |
| 361 | + * Gets the first index within an annotated character that matches a given annotation. |
| 362 | + * |
| 363 | + * Strict coverage may be used to compare not only annotation types, but also their data. |
| 364 | + * |
| 365 | + * @param offset {Integer} Index of character within content data to find annotation within |
| 366 | + * @param annotation {Object} Annotation to compare with |
| 367 | + * @param strict {Boolean} Optionally compare annotation data as well as type |
| 368 | + */ |
| 369 | +Content.prototype.indexOfAnnotation = function( offset, annotation, strict ) { |
| 370 | + var annotatedCharacter = this.data[offset]; |
| 371 | + if ( typeof annotatedCharacter !== 'string' ) { |
| 372 | + for ( var i = 1; i < this.data[offset].length; i++ ) { |
| 373 | + if ( annotatedCharacter[i].type === annotation.type ) { |
| 374 | + if ( strict ) { |
| 375 | + if ( Content.compareObjects( annotatedCharacter[i].data, annotation.data ) ) { |
| 376 | + return i; |
| 377 | + } |
| 378 | + } else { |
| 379 | + return i; |
| 380 | + } |
| 381 | + } |
| 382 | + } |
| 383 | + } |
| 384 | + return -1; |
| 385 | +}; |
| 386 | + |
| 387 | +/** |
| 388 | + * Applies an annotation to content data within a given range. |
| 389 | + * |
| 390 | + * If a range arguments are not provided, all content will be annotated. New line characters are |
| 391 | + * never annotated. The add method will replace and the remove method will delete any existing |
| 392 | + * annotations with the same type as the annotation argument, regardless of their data properties. |
| 393 | + * The toggle method will use add if any of the content within the range is not already covered by |
| 394 | + * the annotation, or remove if all of it is. |
| 395 | + * |
| 396 | + * @param method {String} Way to apply annotation ("toggle", "add" or "remove") |
| 397 | + * @param annotation {Object} Annotation to apply |
| 398 | + * @param start {Integer} Offset to begin annotating from |
| 399 | + * @param end {Integer} Offset to stop annotating to |
| 400 | + */ |
| 401 | +Content.prototype.annotate = function( method, annotation, start, end ) { |
| 402 | + start = Math.max( start, 0 ); |
| 403 | + end = Math.min( end, this.data.length ); |
| 404 | + if ( method === 'toggle' ) { |
| 405 | + var coverage = this.coverageOfAnnotation( start, end, annotation, false ); |
| 406 | + if ( coverage.length === end - start ) { |
| 407 | + var strictCoverage = this.coverageOfAnnotation( start, end, annotation, true ); |
| 408 | + method = strictCoverage.length === coverage.length ? 'remove' : 'add'; |
| 409 | + } else { |
| 410 | + method = 'add'; |
| 411 | + } |
| 412 | + } |
| 413 | + if ( method === 'add' ) { |
| 414 | + var duplicate; |
| 415 | + for ( var i = start; i < end; i++ ) { |
| 416 | + duplicate = -1; |
| 417 | + if ( typeof this.data[i] === 'string' ) { |
| 418 | + // Never annotate new lines |
| 419 | + if ( this.data[i] === '\n' ) { |
| 420 | + continue; |
| 421 | + } |
| 422 | + // Auto-initialize as annotated character |
| 423 | + this.data[i] = [this.data[i]]; |
| 424 | + } else { |
| 425 | + // Detect duplicate annotation |
| 426 | + duplicate = this.indexOfAnnotation( i, annotation ); |
| 427 | + } |
| 428 | + if ( duplicate === -1 ) { |
| 429 | + // Append new annotation |
| 430 | + this.data[i].push( annotation ); |
| 431 | + } else { |
| 432 | + // Replace existing annotation |
| 433 | + this.data[i][duplicate] = annotation; |
| 434 | + } |
| 435 | + } |
| 436 | + } else if ( method === 'remove' ) { |
| 437 | + for ( var i = start; i < end; i++ ) { |
| 438 | + if ( typeof this.data[i] !== 'string' ) { |
| 439 | + if ( annotation.type === 'all' ) { |
| 440 | + // Remove all annotations by converting the annotated character to a plain |
| 441 | + // character |
| 442 | + this.data[i] = this.data[i][0]; |
| 443 | + } |
| 444 | + // Remove all matching instances of annotation |
| 445 | + var j; |
| 446 | + while ( ( j = this.indexOfAnnotation( i, annotation ) ) !== -1 ) { |
| 447 | + this.data[i].splice( j, 1 ); |
| 448 | + } |
| 449 | + } |
| 450 | + } |
| 451 | + } |
| 452 | +}; |
| 453 | + |
| 454 | +/** |
| 455 | + * Gets an HTML rendering of a range of content data. |
| 456 | + * |
| 457 | + * @param start {Integer} Beginning of range |
| 458 | + * @param end {Integer} End of range |
| 459 | + * @param {String} Rendered HTML of content data |
| 460 | + */ |
| 461 | +Content.prototype.render = function( start, end ) { |
| 462 | + if ( start || end ) { |
| 463 | + return this.slice( start, end ).render(); |
| 464 | + } |
| 465 | + var out = '', |
| 466 | + left = '', |
| 467 | + right, |
| 468 | + leftPlain, |
| 469 | + rightPlain, |
| 470 | + stack = []; |
| 471 | + for ( var i = 0; i < this.data.length; i++ ) { |
| 472 | + right = this.data[i]; |
| 473 | + leftPlain = typeof left === 'string'; |
| 474 | + rightPlain = typeof right === 'string'; |
| 475 | + if ( !leftPlain && rightPlain ) { |
| 476 | + // [formatted][plain] pair, close any annotations for left |
| 477 | + for ( var j = 1; j < left.length; j++ ) { |
| 478 | + out += Content.renderAnnotation( 'close', left[j], stack ); |
| 479 | + } |
| 480 | + } else if ( leftPlain && !rightPlain ) { |
| 481 | + // [plain][formatted] pair, open any annotations for right |
| 482 | + for ( var j = 1; j < right.length; j++ ) { |
| 483 | + out += Content.renderAnnotation( 'open', right[j], stack ); |
| 484 | + } |
| 485 | + } else if ( !leftPlain && !rightPlain ) { |
| 486 | + // [formatted][formatted] pair, open/close any differences |
| 487 | + for ( var j = 1; j < left.length; j++ ) { |
| 488 | + if ( right.indexOf( left[j] ) === -1 ) { |
| 489 | + out += Content.renderAnnotation( 'close', left[j], stack ); |
| 490 | + } |
| 491 | + } |
| 492 | + for ( var j = 1; j < right.length; j++ ) { |
| 493 | + if ( left.indexOf( right[j] ) === -1 ) { |
| 494 | + out += Content.renderAnnotation( 'open', right[j], stack ); |
| 495 | + } |
| 496 | + } |
| 497 | + } |
| 498 | + out += right[0] in Content.htmlCharacters ? Content.htmlCharacters[right[0]] : right[0]; |
| 499 | + left = right; |
| 500 | + } |
| 501 | + return out; |
| 502 | +} |
Property changes on: trunk/parsers/wikidom/lib/es/es.Content.js |
___________________________________________________________________ |
Added: svn:eol-style |
503 | 503 | + native |
Index: trunk/parsers/wikidom/lib/es/es.Cursor.js |
— | — | @@ -1,49 +1,49 @@ |
2 | | -/**
|
3 | | - *
|
4 | | - * @returns {Cursor}
|
5 | | - */
|
6 | | -function Cursor() {
|
7 | | - this.cursorInterval = null;
|
8 | | - this.$ = $( '<div class="editSurface-cursor"></div>' );
|
9 | | -}
|
10 | | -
|
11 | | -/**
|
12 | | - * Shows the cursor in a new position.
|
13 | | - *
|
14 | | - * @param position {Position} Position to show the cursor at
|
15 | | - * @param offset {Position} Offset to be added to position
|
16 | | - */
|
17 | | -Cursor.prototype.show = function( position, offset ) {
|
18 | | - if ( position ) {
|
19 | | - if ( $.isPlainObject( offset ) ) {
|
20 | | - position.left += offset.left;
|
21 | | - position.top += offset.top;
|
22 | | - position.bottom += offset.top;
|
23 | | - }
|
24 | | - this.$.css({
|
25 | | - 'left': position.left,
|
26 | | - 'top': position.top,
|
27 | | - 'height': position.bottom - position.top
|
28 | | - }).show();
|
29 | | - } else {
|
30 | | - this.$.show();
|
31 | | - }
|
32 | | -
|
33 | | - if ( this.cursorInterval ) {
|
34 | | - clearInterval( this.cursorInterval );
|
35 | | - }
|
36 | | - this.cursorInterval = setInterval( function( cursor ) {
|
37 | | - cursor.$.css( 'display' ) == 'block'
|
38 | | - ? cursor.$.hide() : cursor.$.show();
|
39 | | - }, 500, this );
|
40 | | -};
|
41 | | -
|
42 | | -/**
|
43 | | - * Hides the cursor.
|
44 | | - */
|
45 | | -Cursor.prototype.hide = function() {
|
46 | | - if( this.cursorInterval ) {
|
47 | | - clearInterval( this.cursorInterval );
|
48 | | - }
|
49 | | - this.$.hide()
|
50 | | -};
|
| 2 | +/** |
| 3 | + * |
| 4 | + * @returns {Cursor} |
| 5 | + */ |
| 6 | +function Cursor() { |
| 7 | + this.cursorInterval = null; |
| 8 | + this.$ = $( '<div class="editSurface-cursor"></div>' ); |
| 9 | +} |
| 10 | + |
| 11 | +/** |
| 12 | + * Shows the cursor in a new position. |
| 13 | + * |
| 14 | + * @param position {Position} Position to show the cursor at |
| 15 | + * @param offset {Position} Offset to be added to position |
| 16 | + */ |
| 17 | +Cursor.prototype.show = function( position, offset ) { |
| 18 | + if ( position ) { |
| 19 | + if ( $.isPlainObject( offset ) ) { |
| 20 | + position.left += offset.left; |
| 21 | + position.top += offset.top; |
| 22 | + position.bottom += offset.top; |
| 23 | + } |
| 24 | + this.$.css({ |
| 25 | + 'left': position.left, |
| 26 | + 'top': position.top, |
| 27 | + 'height': position.bottom - position.top |
| 28 | + }).show(); |
| 29 | + } else { |
| 30 | + this.$.show(); |
| 31 | + } |
| 32 | + |
| 33 | + if ( this.cursorInterval ) { |
| 34 | + clearInterval( this.cursorInterval ); |
| 35 | + } |
| 36 | + this.cursorInterval = setInterval( function( cursor ) { |
| 37 | + cursor.$.css( 'display' ) == 'block' |
| 38 | + ? cursor.$.hide() : cursor.$.show(); |
| 39 | + }, 500, this ); |
| 40 | +}; |
| 41 | + |
| 42 | +/** |
| 43 | + * Hides the cursor. |
| 44 | + */ |
| 45 | +Cursor.prototype.hide = function() { |
| 46 | + if( this.cursorInterval ) { |
| 47 | + clearInterval( this.cursorInterval ); |
| 48 | + } |
| 49 | + this.$.hide() |
| 50 | +}; |
Property changes on: trunk/parsers/wikidom/lib/es/es.Cursor.js |
___________________________________________________________________ |
Added: svn:eol-style |
51 | 51 | + native |
Property changes on: trunk/parsers/wikidom/demos/es/index.html |
___________________________________________________________________ |
Added: svn:eol-style |
52 | 52 | + native |
Property changes on: trunk/parsers/wikidom/demos/surface/index.html |
___________________________________________________________________ |
Added: svn:eol-style |
53 | 53 | + native |
Property changes on: trunk/parsers/wikidom/demos/renderers/index.html |
___________________________________________________________________ |
Added: svn:eol-style |
54 | 54 | + native |
Property changes on: trunk/parsers/wikidom/README |
___________________________________________________________________ |
Added: svn:eol-style |
55 | 55 | + native |