Index: trunk/parsers/wikidom/lib/jquery.flow.js |
— | — | @@ -1,187 +0,0 @@ |
2 | | -/* |
3 | | - * Flow jQuery plugin |
4 | | - * |
5 | | - * Each line has data in the following structure, embedded as $line.data( 'flow' ) |
6 | | - * { |
7 | | - * index: 0, |
8 | | - * text: 'abc 123', |
9 | | - * metrics: [9,4,9] |
10 | | - * width: 22, |
11 | | - * words: [ |
12 | | - * { |
13 | | - * text: 'abc', |
14 | | - * html: 'abc', |
15 | | - * metrics: [3,3,3], |
16 | | - * width: 9, |
17 | | - * index: 0, |
18 | | - * offset: 0 |
19 | | - * }, |
20 | | - * { |
21 | | - * text: ' ', |
22 | | - * html: ' ', |
23 | | - * metrics: [4], |
24 | | - * width: 4, |
25 | | - * index: 1, |
26 | | - * offset: 3 |
27 | | - * }, |
28 | | - * { |
29 | | - * text: '123', |
30 | | - * html: '123', |
31 | | - * metrics: [3,3,3] |
32 | | - * width: 9, |
33 | | - * index: 2, |
34 | | - * offset: 4 |
35 | | - * } |
36 | | - * ] |
37 | | - * } |
38 | | - */ |
39 | | -function copy( from, to ) { |
40 | | - if ( typeof to === 'undefined' ) { |
41 | | - to = {}; |
42 | | - } |
43 | | - if ( from == null || typeof from != 'object' ) { |
44 | | - return from; |
45 | | - } |
46 | | - if ( from.constructor != Object && from.constructor != Array ) { |
47 | | - return from; |
48 | | - } |
49 | | - if ( from.constructor == Date |
50 | | - || from.constructor == RegExp |
51 | | - || from.constructor == Function |
52 | | - || from.constructor == String |
53 | | - || from.constructor == Number |
54 | | - || from.constructor == Boolean ) { |
55 | | - return new from.constructor( from ); |
56 | | - } |
57 | | - to = to || new from.constructor(); |
58 | | - for ( var name in from ) { |
59 | | - to[name] = typeof to[name] == 'undefined' ? copy( from[name], null ) : to[name]; |
60 | | - } |
61 | | - return to; |
62 | | -} |
63 | | - |
64 | | -$.flow = { |
65 | | - 'charCache': {}, |
66 | | - 'wordCache': {}, |
67 | | - 'measureWord': function( text, ruler ) { |
68 | | - if ( typeof $.flow.wordCache[text] === 'undefined' ) { |
69 | | - // Cache miss |
70 | | - var word = { 'text': text, 'html': '', 'metrics': [] }; |
71 | | - for ( var i = 0; i < text.length; i++ ) { |
72 | | - var char = text[i], |
73 | | - charHtml = char |
74 | | - .replace( '&', '&' ) |
75 | | - .replace( ' ', ' ' ) |
76 | | - .replace( '<', '<' ) |
77 | | - .replace( '>', '>' ) |
78 | | - .replace( '\'', ''' ) |
79 | | - .replace( '"', '"' ) |
80 | | - .replace( '\n', '<span class="editSurface-whitespace">\\n</span>' ) |
81 | | - .replace( '\t', '<span class="editSurface-whitespace">\\t</span>' ); |
82 | | - word.html += charHtml; |
83 | | - if ( typeof $.flow.charCache[char] === 'undefined' ) { |
84 | | - // Cache miss |
85 | | - ruler.innerHTML = charHtml; |
86 | | - word.metrics.push( $.flow.charCache[char] = ruler.clientWidth ); |
87 | | - continue; |
88 | | - } |
89 | | - // Cache hit |
90 | | - word.metrics.push( $.flow.charCache[char] ); |
91 | | - } |
92 | | - ruler.innerHTML = word.html; |
93 | | - word.width = ruler.clientWidth; |
94 | | - $.flow.wordCache[text] = copy( word ); |
95 | | - return word; |
96 | | - } |
97 | | - // Cache hit |
98 | | - return copy( $.flow.wordCache[text] ); |
99 | | - }, |
100 | | - 'getWords': function( text, ruler ) { |
101 | | - var words = [], |
102 | | - bounadry = /[ \-\t\r\n\f]/, |
103 | | - left = 0, |
104 | | - right = 0, |
105 | | - search = 0; |
106 | | - while ( ( search = text.substr( right ).search( bounadry ) ) >= 0 ) { |
107 | | - right += search; |
108 | | - words.push( $.flow.measureWord( text.substring( left, right ), ruler ) ); |
109 | | - if ( right < text.length ) { |
110 | | - words.push( $.flow.measureWord( text.substring( right, ++right ), ruler ) ); |
111 | | - } |
112 | | - left = right; |
113 | | - } |
114 | | - words.push( $.flow.measureWord( text.substring( right, text.length ), ruler ) ); |
115 | | - return words; |
116 | | - }, |
117 | | - 'getLines': function( words, width ) { |
118 | | - // Lineify |
119 | | - var lineCount = 0, |
120 | | - charCount = 0, |
121 | | - wordCount = 0, |
122 | | - lines = [], |
123 | | - line = { |
124 | | - 'text': '', |
125 | | - 'html': '', |
126 | | - 'width': 0, |
127 | | - 'metrics': [], |
128 | | - 'words': [], |
129 | | - 'index': lineCount |
130 | | - }; |
131 | | - for ( var i = 0; i < words.length; i++ ) { |
132 | | - if ( line.width + words[i].width > width ) { |
133 | | - lines.push( line ); |
134 | | - charCount = 0; |
135 | | - wordCount = 0; |
136 | | - lineCount++; |
137 | | - line = { |
138 | | - 'text': '', |
139 | | - 'html': '', |
140 | | - 'width': 0, |
141 | | - 'metrics': [], |
142 | | - 'words': [], |
143 | | - 'index': lineCount |
144 | | - }; |
145 | | - } |
146 | | - words[i].index = wordCount; |
147 | | - wordCount++; |
148 | | - words[i].offset = charCount; |
149 | | - charCount += words[i].text.length; |
150 | | - line.words.push( words[i] ); |
151 | | - line.text += words[i].text; |
152 | | - line.html += words[i].html; |
153 | | - line.width += words[i].width; |
154 | | - line.metrics.push( words[i].width ); |
155 | | - } |
156 | | - if ( line.text.length ) { |
157 | | - lines.push( line ); |
158 | | - } |
159 | | - return lines; |
160 | | - } |
161 | | -}; |
162 | | - |
163 | | -$.fn.flow = function( text ) { |
164 | | - //console.time( 'flow' ); |
165 | | - |
166 | | - var $this = $(this), |
167 | | - $ruler = $( '<div></div>' ).appendTo( $(this) ), |
168 | | - lines = $.flow.getLines( |
169 | | - $.flow.getWords( text, $( '<div class="editSurface-line"></div>' ).appendTo( $this )[0] ), |
170 | | - $ruler.innerWidth() |
171 | | - ); |
172 | | - |
173 | | - // Flow |
174 | | - $this.empty(); |
175 | | - for ( var i = 0; i < lines.length; i++ ) { |
176 | | - var $line = $( '<div class="editSurface-line"></div>' ).data( 'flow', lines[i] ); |
177 | | - if ( lines[i].text.length === 1 && lines[1].text.match( /[ \-\t\r\n\f]/ ) ) { |
178 | | - $line.html( ' ' ); |
179 | | - $line.addClass( 'empty' ); |
180 | | - } else { |
181 | | - $line.html( lines[i].html ); |
182 | | - } |
183 | | - $this.append( $line ); |
184 | | - } |
185 | | - |
186 | | - //console.timeEnd( 'flow' ); |
187 | | - return $this; |
188 | | -}; |
Index: trunk/parsers/wikidom/lib/jquery.editSurface.css |
— | — | @@ -1,65 +0,0 @@ |
2 | | -body { |
3 | | - font-family: "Arial"; |
4 | | - font-size: 0.8em; |
5 | | -} |
6 | | - |
7 | | -#selection { |
8 | | - position: absolute; |
9 | | - right: 3%; |
10 | | - width: 45%; |
11 | | - border: solid 1px Highlight; |
12 | | - line-height: 1.5em; |
13 | | - cursor: text; |
14 | | - font-family: "Courier New", monospace; |
15 | | - white-space: pre-wrap; |
16 | | -} |
17 | | - |
18 | | -#selection p { |
19 | | - margin: 0; |
20 | | - padding: 2em; |
21 | | -} |
22 | | - |
23 | | -.editSurface-container { |
24 | | - position: absolute; |
25 | | - left: 3%; |
26 | | - width: 45%; |
27 | | - border: solid 1px red; |
28 | | - cursor: text; |
29 | | -} |
30 | | - |
31 | | -.editSurface-paragraph { |
32 | | - padding: 2em; |
33 | | -} |
34 | | - |
35 | | -.editSurface-line { |
36 | | - display: inline-block; |
37 | | - height: 1.5em; |
38 | | - line-height: 1.5em; |
39 | | - cursor: text; |
40 | | - white-space: nowrap; |
41 | | -} |
42 | | - |
43 | | -.editSurface-line.empty { |
44 | | - display: block; |
45 | | - width: 0px; |
46 | | -} |
47 | | - |
48 | | -.editSurface-line .editSurface-whitespace { |
49 | | - color: #888888; |
50 | | - padding: 0 0.25em; |
51 | | -} |
52 | | - |
53 | | -.editSurface-range { |
54 | | - display: none; |
55 | | - position: absolute; |
56 | | - background-color: Highlight; |
57 | | - cursor: text; |
58 | | -} |
59 | | - |
60 | | -.editSurface-cursor { |
61 | | - position: absolute; |
62 | | - background-color: black; |
63 | | - width: 1px; |
64 | | - height: 1.5em; |
65 | | - display: none; |
66 | | -} |
Index: trunk/parsers/wikidom/lib/jquery.editSurface.js |
— | — | @@ -1,293 +0,0 @@ |
2 | | -$.fn.editSurface = function( options ) { |
3 | | - var $this = $(this); |
4 | | - sel = { |
5 | | - 'active': false |
6 | | - }; |
7 | | - |
8 | | - options = $.extend( { |
9 | | - // Defaults |
10 | | - 'document': null |
11 | | - }, options ); |
12 | | - |
13 | | - // Initialization |
14 | | - $this |
15 | | - .addClass( 'editSurface-container' ) |
16 | | - .append( '<div class="editSurface-document"></div>' ) |
17 | | - .after( '<div class="editSurface-cursor"></div>' ) |
18 | | - .before( '<div class="editSurface-range"></div>' |
19 | | - + '<div class="editSurface-range"></div>' |
20 | | - + '<div class="editSurface-range"></div>'); |
21 | | - |
22 | | - // Shortcuts |
23 | | - var $document = $this.find( '.editSurface-document' ); |
24 | | - var ranges = { |
25 | | - '$all': $( '.editSurface-range' ), |
26 | | - '$first': $( '.editSurface-range:eq(0)' ), |
27 | | - '$fill': $( '.editSurface-range:eq(1)' ), |
28 | | - '$last': $( '.editSurface-range:eq(2)' ) |
29 | | - }; |
30 | | - |
31 | | - // Events |
32 | | - $(document).bind( { |
33 | | - 'mousedown': function( e ) { |
34 | | - var $target = $( e.target ); |
35 | | - if ( $target.is( '.editSurface-paragraph' ) ) { |
36 | | - $target = getNearestLine( $target.children(), e.pageY ); |
37 | | - } |
38 | | - if ( !$target.is( '.editSurface-line' ) ) { |
39 | | - var $line = $target.closest( '.editSurface-line' ); |
40 | | - if ( $line.length ) { |
41 | | - $target = $line; |
42 | | - } else { |
43 | | - return; |
44 | | - } |
45 | | - } |
46 | | - sel = { |
47 | | - 'active': true, |
48 | | - 'from': null, |
49 | | - 'to': null, |
50 | | - 'start': getCursorPosition( e.pageX, e.pageY, $target ), |
51 | | - 'end': null |
52 | | - }; |
53 | | - cursor.show(); |
54 | | - drawSelection( sel.start.$target.parent() ); |
55 | | - // Move cursor |
56 | | - if ( sel.start ) { |
57 | | - cursor.$.css( { |
58 | | - 'top': sel.start.top, |
59 | | - 'left': sel.start.x |
60 | | - } ); |
61 | | - } |
62 | | - e.preventDefault(); |
63 | | - return false; |
64 | | - }, |
65 | | - 'mouseup': function( e ) { |
66 | | - if ( sel.active ) { |
67 | | - if ( !sel.from || !sel.to |
68 | | - || ( sel.from.line === sel.to.line && sel.from.char === sel.to.char ) ) { |
69 | | - sel.from = null; |
70 | | - sel.to = null; |
71 | | - sel.start = null; |
72 | | - sel.end = null; |
73 | | - cursor.show(); |
74 | | - clearSelection(); |
75 | | - } else { |
76 | | - drawSelection( sel.start.$target.parent() ); |
77 | | - } |
78 | | - sel.active = false; |
79 | | - } |
80 | | - }, |
81 | | - 'mousemove': function( e ) { |
82 | | - if ( sel.active ) { |
83 | | - var $target = $( e.target ); |
84 | | - if ( !$target.is( '.editSurface-line' ) ) { |
85 | | - $target = getNearestLine( sel.start.$target.parent().children(), e.pageY ); |
86 | | - } |
87 | | - sel.end = getCursorPosition( e.pageX, e.pageY, $target ); |
88 | | - if ( sel.start.line < sel.end.line |
89 | | - || ( sel.start.line === sel.end.line |
90 | | - && sel.start.char < sel.end.char ) ) { |
91 | | - sel.from = sel.start; |
92 | | - sel.to = sel.end; |
93 | | - } else { |
94 | | - sel.from = sel.end; |
95 | | - sel.to = sel.start; |
96 | | - } |
97 | | - cursor.hide(); |
98 | | - drawSelection( sel.start.$target.parent() ); |
99 | | - } |
100 | | - } |
101 | | - } ); |
102 | | - |
103 | | - // Functions |
104 | | - function getNearestLine( $lines, y ) { |
105 | | - var $line, |
106 | | - minDistance; |
107 | | - $lines.each( function() { |
108 | | - var top = $(this).offset().top; |
109 | | - var bottom = top + $(this).height(); |
110 | | - // Inside test |
111 | | - if ( y > top && y < bottom ) { |
112 | | - $line = $(this); |
113 | | - return false; |
114 | | - } |
115 | | - // Distance test |
116 | | - var distance = Math.abs( y - top ); |
117 | | - if ( typeof minDistance === 'undefined' || distance < minDistance ) { |
118 | | - minDistance = distance; |
119 | | - $line = $(this); |
120 | | - } |
121 | | - } ); |
122 | | - return $line; |
123 | | - } |
124 | | - function getSelectionText() { |
125 | | - var text; |
126 | | - if ( sel.from && sel.to ) { |
127 | | - if ( sel.from.line === sel.to.line ) { |
128 | | - text = sel.from.$target.data( 'flow' ).text.substr( |
129 | | - sel.from.char, sel.to.char - sel.from.char |
130 | | - ); |
131 | | - } else { |
132 | | - text = sel.from.$target.data( 'flow' ).text.substr( sel.from.char ); |
133 | | - var $sibling = sel.from.$target.next(); |
134 | | - for ( var i = sel.from.line + 1; i < sel.to.line; i++ ) { |
135 | | - text += $sibling.data( 'flow' ).text |
136 | | - $sibling = $sibling.next(); |
137 | | - } |
138 | | - text += sel.to.$target.data( 'flow' ).text.substr( 0, sel.to.char ); |
139 | | - } |
140 | | - } |
141 | | - return text; |
142 | | - } |
143 | | - function getCursorPosition( x, y, $target ) { |
144 | | - var line = $target.data( 'flow' ), |
145 | | - offset = $target.offset(), |
146 | | - height = $target.height(), |
147 | | - l, |
148 | | - r = 0, |
149 | | - cur = x - offset.left; |
150 | | - for ( var w = 0, eol = line.metrics.length - 1; w <= eol; w++ ) { |
151 | | - l = r; |
152 | | - r += line.metrics[w]; |
153 | | - if ( ( w === 0 && cur <= l ) || ( cur >= l && cur <= r ) || ( w === eol ) ) { |
154 | | - var word = line.words[w], |
155 | | - a, |
156 | | - b = { 'l': l, 'c': l, 'r': l }; |
157 | | - for ( var c = 0, eow = word.metrics.length; c <= eow; c++ ) { |
158 | | - a = b; |
159 | | - b = { |
160 | | - 'l': a.r, |
161 | | - 'c': a.r + ( word.metrics[c] / 2 ), |
162 | | - 'r': a.r + word.metrics[c] |
163 | | - }; |
164 | | - if ( ( c === 0 && cur <= a.l ) || ( cur >= a.c && cur <= b.c ) || c === eow ) { |
165 | | - return { |
166 | | - '$target': $target, |
167 | | - 'char': word.offset + Math.min( c, word.text.length - 1 ), |
168 | | - 'word': word.index, |
169 | | - 'line': line.index, |
170 | | - 'x': offset.left + b.l, |
171 | | - 'top': offset.top, |
172 | | - 'bottom': offset.top + height, |
173 | | - 'height': height |
174 | | - }; |
175 | | - } |
176 | | - } |
177 | | - } |
178 | | - } |
179 | | - } |
180 | | - |
181 | | - function clearSelection() { |
182 | | - ranges.$all.hide(); |
183 | | - } |
184 | | - |
185 | | - function drawSelection( $container ) { |
186 | | - if ( sel.from && sel.to ) { |
187 | | - if ( sel.from.line === sel.to.line ) { |
188 | | - // 1 line |
189 | | - if ( sel.from.char !== sel.to.char ) { |
190 | | - ranges.$first.show().css( { |
191 | | - 'left': sel.from.x, |
192 | | - 'top': sel.from.top, |
193 | | - 'width': sel.to.x - sel.from.x, |
194 | | - 'height': sel.from.height |
195 | | - } ); |
196 | | - ranges.$fill.hide(); |
197 | | - ranges.$last.hide(); |
198 | | - // XXX: Demo code! |
199 | | - $( '#selection p' ).text( getSelectionText() ); |
200 | | - return; |
201 | | - } |
202 | | - } else if ( sel.from.line < sel.to.line ) { |
203 | | - // 2+ lines |
204 | | - ranges.$first.show().css( { |
205 | | - 'left': sel.from.x, |
206 | | - 'top': sel.from.top, |
207 | | - 'width': ( $container.innerWidth() - sel.from.x ) |
208 | | - + $container.offset().left, |
209 | | - 'height': sel.from.height |
210 | | - } ); |
211 | | - if ( sel.from.line < sel.to.line - 1 ) { |
212 | | - ranges.$fill.show().css( { |
213 | | - 'left': $container.offset().left, |
214 | | - 'top': sel.from.bottom, |
215 | | - 'width': $container.innerWidth(), |
216 | | - 'height': sel.to.top - sel.from.bottom |
217 | | - } ); |
218 | | - } else { |
219 | | - ranges.$fill.hide(); |
220 | | - } |
221 | | - ranges.$last.show().css( { |
222 | | - 'left': $container.offset().left, |
223 | | - 'top': sel.to.top, |
224 | | - 'width': sel.to.x - $container.offset().left, |
225 | | - 'height': sel.to.height |
226 | | - } ); |
227 | | - // XXX: Demo code! |
228 | | - $( '#selection p' ).text( getSelectionText() ); |
229 | | - return; |
230 | | - } |
231 | | - } |
232 | | - // No selection |
233 | | - ranges.$all.hide(); |
234 | | - } |
235 | | - |
236 | | - function renderDocument( doc ) { |
237 | | - $document.empty(); |
238 | | - for ( var i = 0; i < doc.blocks.length; i++ ) { |
239 | | - switch ( doc.blocks[i].type ) { |
240 | | - case 'paragraph': |
241 | | - renderParagraph( doc.blocks[i], $document ) |
242 | | - break; |
243 | | - } |
244 | | - } |
245 | | - } |
246 | | - |
247 | | - function renderParagraph( paragraph, $container ) { |
248 | | - var $paragraph = $( '<div class="editSurface-paragraph"></div>' ).appendTo( $container ); |
249 | | - var lines = []; |
250 | | - for ( var i = 0; i < paragraph.lines.length; i++ ) { |
251 | | - lines.push( paragraph.lines[i].text ); |
252 | | - } |
253 | | - $paragraph.flow( lines.join( '\n' ) ); |
254 | | - } |
255 | | - |
256 | | - function update() { |
257 | | - // Render document |
258 | | - if ( options.document !== null ) { |
259 | | - renderDocument( options.document, $this ); |
260 | | - } |
261 | | - } |
262 | | - |
263 | | - var cursor = { |
264 | | - '$': $( '.editSurface-cursor' ), |
265 | | - 'visible': true, |
266 | | - 'timeout': null, |
267 | | - 'speed': 500 |
268 | | - }; |
269 | | - cursor.blink = function() { |
270 | | - // Flip |
271 | | - cursor.visible = !cursor.visible; |
272 | | - // Hide/show |
273 | | - cursor.visible ? cursor.$.hide() : cursor.$.show(); |
274 | | - // Repeat |
275 | | - cursor.timeout = setTimeout( cursor.blink, cursor.speed ) |
276 | | - } |
277 | | - cursor.show = function() { |
278 | | - // Start visible (will flip when run) |
279 | | - cursor.visible = true; |
280 | | - cursor.$.show(); |
281 | | - // Start/restart blinking |
282 | | - clearTimeout( cursor.timeout ); |
283 | | - cursor.blink(); |
284 | | - }; |
285 | | - cursor.hide = function() { |
286 | | - // Hide |
287 | | - cursor.$.hide(); |
288 | | - // Stop blinking |
289 | | - clearTimeout( cursor.timeout ); |
290 | | - }; |
291 | | - |
292 | | - $(window).resize( update ); |
293 | | - update(); |
294 | | -}; |
Index: trunk/parsers/wikidom/demos/surface/jquery.flow.js |
— | — | @@ -0,0 +1,187 @@ |
| 2 | +/* |
| 3 | + * Flow jQuery plugin |
| 4 | + * |
| 5 | + * Each line has data in the following structure, embedded as $line.data( 'flow' ) |
| 6 | + * { |
| 7 | + * index: 0, |
| 8 | + * text: 'abc 123', |
| 9 | + * metrics: [9,4,9] |
| 10 | + * width: 22, |
| 11 | + * words: [ |
| 12 | + * { |
| 13 | + * text: 'abc', |
| 14 | + * html: 'abc', |
| 15 | + * metrics: [3,3,3], |
| 16 | + * width: 9, |
| 17 | + * index: 0, |
| 18 | + * offset: 0 |
| 19 | + * }, |
| 20 | + * { |
| 21 | + * text: ' ', |
| 22 | + * html: ' ', |
| 23 | + * metrics: [4], |
| 24 | + * width: 4, |
| 25 | + * index: 1, |
| 26 | + * offset: 3 |
| 27 | + * }, |
| 28 | + * { |
| 29 | + * text: '123', |
| 30 | + * html: '123', |
| 31 | + * metrics: [3,3,3] |
| 32 | + * width: 9, |
| 33 | + * index: 2, |
| 34 | + * offset: 4 |
| 35 | + * } |
| 36 | + * ] |
| 37 | + * } |
| 38 | + */ |
| 39 | +function copy( from, to ) { |
| 40 | + if ( typeof to === 'undefined' ) { |
| 41 | + to = {}; |
| 42 | + } |
| 43 | + if ( from == null || typeof from != 'object' ) { |
| 44 | + return from; |
| 45 | + } |
| 46 | + if ( from.constructor != Object && from.constructor != Array ) { |
| 47 | + return from; |
| 48 | + } |
| 49 | + if ( from.constructor == Date |
| 50 | + || from.constructor == RegExp |
| 51 | + || from.constructor == Function |
| 52 | + || from.constructor == String |
| 53 | + || from.constructor == Number |
| 54 | + || from.constructor == Boolean ) { |
| 55 | + return new from.constructor( from ); |
| 56 | + } |
| 57 | + to = to || new from.constructor(); |
| 58 | + for ( var name in from ) { |
| 59 | + to[name] = typeof to[name] == 'undefined' ? copy( from[name], null ) : to[name]; |
| 60 | + } |
| 61 | + return to; |
| 62 | +} |
| 63 | + |
| 64 | +$.flow = { |
| 65 | + 'charCache': {}, |
| 66 | + 'wordCache': {}, |
| 67 | + 'measureWord': function( text, ruler ) { |
| 68 | + if ( typeof $.flow.wordCache[text] === 'undefined' ) { |
| 69 | + // Cache miss |
| 70 | + var word = { 'text': text, 'html': '', 'metrics': [] }; |
| 71 | + for ( var i = 0; i < text.length; i++ ) { |
| 72 | + var char = text[i], |
| 73 | + charHtml = char |
| 74 | + .replace( '&', '&' ) |
| 75 | + .replace( ' ', ' ' ) |
| 76 | + .replace( '<', '<' ) |
| 77 | + .replace( '>', '>' ) |
| 78 | + .replace( '\'', ''' ) |
| 79 | + .replace( '"', '"' ) |
| 80 | + .replace( '\n', '<span class="editSurface-whitespace">\\n</span>' ) |
| 81 | + .replace( '\t', '<span class="editSurface-whitespace">\\t</span>' ); |
| 82 | + word.html += charHtml; |
| 83 | + if ( typeof $.flow.charCache[char] === 'undefined' ) { |
| 84 | + // Cache miss |
| 85 | + ruler.innerHTML = charHtml; |
| 86 | + word.metrics.push( $.flow.charCache[char] = ruler.clientWidth ); |
| 87 | + continue; |
| 88 | + } |
| 89 | + // Cache hit |
| 90 | + word.metrics.push( $.flow.charCache[char] ); |
| 91 | + } |
| 92 | + ruler.innerHTML = word.html; |
| 93 | + word.width = ruler.clientWidth; |
| 94 | + $.flow.wordCache[text] = copy( word ); |
| 95 | + return word; |
| 96 | + } |
| 97 | + // Cache hit |
| 98 | + return copy( $.flow.wordCache[text] ); |
| 99 | + }, |
| 100 | + 'getWords': function( text, ruler ) { |
| 101 | + var words = [], |
| 102 | + bounadry = /[ \-\t\r\n\f]/, |
| 103 | + left = 0, |
| 104 | + right = 0, |
| 105 | + search = 0; |
| 106 | + while ( ( search = text.substr( right ).search( bounadry ) ) >= 0 ) { |
| 107 | + right += search; |
| 108 | + words.push( $.flow.measureWord( text.substring( left, right ), ruler ) ); |
| 109 | + if ( right < text.length ) { |
| 110 | + words.push( $.flow.measureWord( text.substring( right, ++right ), ruler ) ); |
| 111 | + } |
| 112 | + left = right; |
| 113 | + } |
| 114 | + words.push( $.flow.measureWord( text.substring( right, text.length ), ruler ) ); |
| 115 | + return words; |
| 116 | + }, |
| 117 | + 'getLines': function( words, width ) { |
| 118 | + // Lineify |
| 119 | + var lineCount = 0, |
| 120 | + charCount = 0, |
| 121 | + wordCount = 0, |
| 122 | + lines = [], |
| 123 | + line = { |
| 124 | + 'text': '', |
| 125 | + 'html': '', |
| 126 | + 'width': 0, |
| 127 | + 'metrics': [], |
| 128 | + 'words': [], |
| 129 | + 'index': lineCount |
| 130 | + }; |
| 131 | + for ( var i = 0; i < words.length; i++ ) { |
| 132 | + if ( line.width + words[i].width > width ) { |
| 133 | + lines.push( line ); |
| 134 | + charCount = 0; |
| 135 | + wordCount = 0; |
| 136 | + lineCount++; |
| 137 | + line = { |
| 138 | + 'text': '', |
| 139 | + 'html': '', |
| 140 | + 'width': 0, |
| 141 | + 'metrics': [], |
| 142 | + 'words': [], |
| 143 | + 'index': lineCount |
| 144 | + }; |
| 145 | + } |
| 146 | + words[i].index = wordCount; |
| 147 | + wordCount++; |
| 148 | + words[i].offset = charCount; |
| 149 | + charCount += words[i].text.length; |
| 150 | + line.words.push( words[i] ); |
| 151 | + line.text += words[i].text; |
| 152 | + line.html += words[i].html; |
| 153 | + line.width += words[i].width; |
| 154 | + line.metrics.push( words[i].width ); |
| 155 | + } |
| 156 | + if ( line.text.length ) { |
| 157 | + lines.push( line ); |
| 158 | + } |
| 159 | + return lines; |
| 160 | + } |
| 161 | +}; |
| 162 | + |
| 163 | +$.fn.flow = function( text ) { |
| 164 | + //console.time( 'flow' ); |
| 165 | + |
| 166 | + var $this = $(this), |
| 167 | + $ruler = $( '<div></div>' ).appendTo( $(this) ), |
| 168 | + lines = $.flow.getLines( |
| 169 | + $.flow.getWords( text, $( '<div class="editSurface-line"></div>' ).appendTo( $this )[0] ), |
| 170 | + $ruler.innerWidth() |
| 171 | + ); |
| 172 | + |
| 173 | + // Flow |
| 174 | + $this.empty(); |
| 175 | + for ( var i = 0; i < lines.length; i++ ) { |
| 176 | + var $line = $( '<div class="editSurface-line"></div>' ).data( 'flow', lines[i] ); |
| 177 | + if ( lines[i].text.length === 1 && lines[1].text.match( /[ \-\t\r\n\f]/ ) ) { |
| 178 | + $line.html( ' ' ); |
| 179 | + $line.addClass( 'empty' ); |
| 180 | + } else { |
| 181 | + $line.html( lines[i].html ); |
| 182 | + } |
| 183 | + $this.append( $line ); |
| 184 | + } |
| 185 | + |
| 186 | + //console.timeEnd( 'flow' ); |
| 187 | + return $this; |
| 188 | +}; |
Property changes on: trunk/parsers/wikidom/demos/surface/jquery.flow.js |
___________________________________________________________________ |
Added: svn:eol-style |
1 | 189 | + native |
Added: svn:mime-type |
2 | 190 | + text/plain |
Index: trunk/parsers/wikidom/demos/surface/jquery.editSurface.css |
— | — | @@ -0,0 +1,65 @@ |
| 2 | +body { |
| 3 | + font-family: "Arial"; |
| 4 | + font-size: 0.8em; |
| 5 | +} |
| 6 | + |
| 7 | +#selection { |
| 8 | + position: absolute; |
| 9 | + right: 3%; |
| 10 | + width: 45%; |
| 11 | + border: solid 1px Highlight; |
| 12 | + line-height: 1.5em; |
| 13 | + cursor: text; |
| 14 | + font-family: "Courier New", monospace; |
| 15 | + white-space: pre-wrap; |
| 16 | +} |
| 17 | + |
| 18 | +#selection p { |
| 19 | + margin: 0; |
| 20 | + padding: 2em; |
| 21 | +} |
| 22 | + |
| 23 | +.editSurface-container { |
| 24 | + position: absolute; |
| 25 | + left: 3%; |
| 26 | + width: 45%; |
| 27 | + border: solid 1px red; |
| 28 | + cursor: text; |
| 29 | +} |
| 30 | + |
| 31 | +.editSurface-paragraph { |
| 32 | + padding: 2em; |
| 33 | +} |
| 34 | + |
| 35 | +.editSurface-line { |
| 36 | + display: inline-block; |
| 37 | + height: 1.5em; |
| 38 | + line-height: 1.5em; |
| 39 | + cursor: text; |
| 40 | + white-space: nowrap; |
| 41 | +} |
| 42 | + |
| 43 | +.editSurface-line.empty { |
| 44 | + display: block; |
| 45 | + width: 0px; |
| 46 | +} |
| 47 | + |
| 48 | +.editSurface-line .editSurface-whitespace { |
| 49 | + color: #888888; |
| 50 | + padding: 0 0.25em; |
| 51 | +} |
| 52 | + |
| 53 | +.editSurface-range { |
| 54 | + display: none; |
| 55 | + position: absolute; |
| 56 | + background-color: Highlight; |
| 57 | + cursor: text; |
| 58 | +} |
| 59 | + |
| 60 | +.editSurface-cursor { |
| 61 | + position: absolute; |
| 62 | + background-color: black; |
| 63 | + width: 1px; |
| 64 | + height: 1.5em; |
| 65 | + display: none; |
| 66 | +} |
Property changes on: trunk/parsers/wikidom/demos/surface/jquery.editSurface.css |
___________________________________________________________________ |
Added: svn:eol-style |
1 | 67 | + native |
Added: svn:mime-type |
2 | 68 | + text/plain |
Index: trunk/parsers/wikidom/demos/surface/jquery.editSurface.js |
— | — | @@ -0,0 +1,293 @@ |
| 2 | +$.fn.editSurface = function( options ) { |
| 3 | + var $this = $(this); |
| 4 | + sel = { |
| 5 | + 'active': false |
| 6 | + }; |
| 7 | + |
| 8 | + options = $.extend( { |
| 9 | + // Defaults |
| 10 | + 'document': null |
| 11 | + }, options ); |
| 12 | + |
| 13 | + // Initialization |
| 14 | + $this |
| 15 | + .addClass( 'editSurface-container' ) |
| 16 | + .append( '<div class="editSurface-document"></div>' ) |
| 17 | + .after( '<div class="editSurface-cursor"></div>' ) |
| 18 | + .before( '<div class="editSurface-range"></div>' |
| 19 | + + '<div class="editSurface-range"></div>' |
| 20 | + + '<div class="editSurface-range"></div>'); |
| 21 | + |
| 22 | + // Shortcuts |
| 23 | + var $document = $this.find( '.editSurface-document' ); |
| 24 | + var ranges = { |
| 25 | + '$all': $( '.editSurface-range' ), |
| 26 | + '$first': $( '.editSurface-range:eq(0)' ), |
| 27 | + '$fill': $( '.editSurface-range:eq(1)' ), |
| 28 | + '$last': $( '.editSurface-range:eq(2)' ) |
| 29 | + }; |
| 30 | + |
| 31 | + // Events |
| 32 | + $(document).bind( { |
| 33 | + 'mousedown': function( e ) { |
| 34 | + var $target = $( e.target ); |
| 35 | + if ( $target.is( '.editSurface-paragraph' ) ) { |
| 36 | + $target = getNearestLine( $target.children(), e.pageY ); |
| 37 | + } |
| 38 | + if ( !$target.is( '.editSurface-line' ) ) { |
| 39 | + var $line = $target.closest( '.editSurface-line' ); |
| 40 | + if ( $line.length ) { |
| 41 | + $target = $line; |
| 42 | + } else { |
| 43 | + return; |
| 44 | + } |
| 45 | + } |
| 46 | + sel = { |
| 47 | + 'active': true, |
| 48 | + 'from': null, |
| 49 | + 'to': null, |
| 50 | + 'start': getCursorPosition( e.pageX, e.pageY, $target ), |
| 51 | + 'end': null |
| 52 | + }; |
| 53 | + cursor.show(); |
| 54 | + drawSelection( sel.start.$target.parent() ); |
| 55 | + // Move cursor |
| 56 | + if ( sel.start ) { |
| 57 | + cursor.$.css( { |
| 58 | + 'top': sel.start.top, |
| 59 | + 'left': sel.start.x |
| 60 | + } ); |
| 61 | + } |
| 62 | + e.preventDefault(); |
| 63 | + return false; |
| 64 | + }, |
| 65 | + 'mouseup': function( e ) { |
| 66 | + if ( sel.active ) { |
| 67 | + if ( !sel.from || !sel.to |
| 68 | + || ( sel.from.line === sel.to.line && sel.from.char === sel.to.char ) ) { |
| 69 | + sel.from = null; |
| 70 | + sel.to = null; |
| 71 | + sel.start = null; |
| 72 | + sel.end = null; |
| 73 | + cursor.show(); |
| 74 | + clearSelection(); |
| 75 | + } else { |
| 76 | + drawSelection( sel.start.$target.parent() ); |
| 77 | + } |
| 78 | + sel.active = false; |
| 79 | + } |
| 80 | + }, |
| 81 | + 'mousemove': function( e ) { |
| 82 | + if ( sel.active ) { |
| 83 | + var $target = $( e.target ); |
| 84 | + if ( !$target.is( '.editSurface-line' ) ) { |
| 85 | + $target = getNearestLine( sel.start.$target.parent().children(), e.pageY ); |
| 86 | + } |
| 87 | + sel.end = getCursorPosition( e.pageX, e.pageY, $target ); |
| 88 | + if ( sel.start.line < sel.end.line |
| 89 | + || ( sel.start.line === sel.end.line |
| 90 | + && sel.start.char < sel.end.char ) ) { |
| 91 | + sel.from = sel.start; |
| 92 | + sel.to = sel.end; |
| 93 | + } else { |
| 94 | + sel.from = sel.end; |
| 95 | + sel.to = sel.start; |
| 96 | + } |
| 97 | + cursor.hide(); |
| 98 | + drawSelection( sel.start.$target.parent() ); |
| 99 | + } |
| 100 | + } |
| 101 | + } ); |
| 102 | + |
| 103 | + // Functions |
| 104 | + function getNearestLine( $lines, y ) { |
| 105 | + var $line, |
| 106 | + minDistance; |
| 107 | + $lines.each( function() { |
| 108 | + var top = $(this).offset().top; |
| 109 | + var bottom = top + $(this).height(); |
| 110 | + // Inside test |
| 111 | + if ( y > top && y < bottom ) { |
| 112 | + $line = $(this); |
| 113 | + return false; |
| 114 | + } |
| 115 | + // Distance test |
| 116 | + var distance = Math.abs( y - top ); |
| 117 | + if ( typeof minDistance === 'undefined' || distance < minDistance ) { |
| 118 | + minDistance = distance; |
| 119 | + $line = $(this); |
| 120 | + } |
| 121 | + } ); |
| 122 | + return $line; |
| 123 | + } |
| 124 | + function getSelectionText() { |
| 125 | + var text; |
| 126 | + if ( sel.from && sel.to ) { |
| 127 | + if ( sel.from.line === sel.to.line ) { |
| 128 | + text = sel.from.$target.data( 'flow' ).text.substr( |
| 129 | + sel.from.char, sel.to.char - sel.from.char |
| 130 | + ); |
| 131 | + } else { |
| 132 | + text = sel.from.$target.data( 'flow' ).text.substr( sel.from.char ); |
| 133 | + var $sibling = sel.from.$target.next(); |
| 134 | + for ( var i = sel.from.line + 1; i < sel.to.line; i++ ) { |
| 135 | + text += $sibling.data( 'flow' ).text |
| 136 | + $sibling = $sibling.next(); |
| 137 | + } |
| 138 | + text += sel.to.$target.data( 'flow' ).text.substr( 0, sel.to.char ); |
| 139 | + } |
| 140 | + } |
| 141 | + return text; |
| 142 | + } |
| 143 | + function getCursorPosition( x, y, $target ) { |
| 144 | + var line = $target.data( 'flow' ), |
| 145 | + offset = $target.offset(), |
| 146 | + height = $target.height(), |
| 147 | + l, |
| 148 | + r = 0, |
| 149 | + cur = x - offset.left; |
| 150 | + for ( var w = 0, eol = line.metrics.length - 1; w <= eol; w++ ) { |
| 151 | + l = r; |
| 152 | + r += line.metrics[w]; |
| 153 | + if ( ( w === 0 && cur <= l ) || ( cur >= l && cur <= r ) || ( w === eol ) ) { |
| 154 | + var word = line.words[w], |
| 155 | + a, |
| 156 | + b = { 'l': l, 'c': l, 'r': l }; |
| 157 | + for ( var c = 0, eow = word.metrics.length; c <= eow; c++ ) { |
| 158 | + a = b; |
| 159 | + b = { |
| 160 | + 'l': a.r, |
| 161 | + 'c': a.r + ( word.metrics[c] / 2 ), |
| 162 | + 'r': a.r + word.metrics[c] |
| 163 | + }; |
| 164 | + if ( ( c === 0 && cur <= a.l ) || ( cur >= a.c && cur <= b.c ) || c === eow ) { |
| 165 | + return { |
| 166 | + '$target': $target, |
| 167 | + 'char': word.offset + Math.min( c, word.text.length - 1 ), |
| 168 | + 'word': word.index, |
| 169 | + 'line': line.index, |
| 170 | + 'x': offset.left + b.l, |
| 171 | + 'top': offset.top, |
| 172 | + 'bottom': offset.top + height, |
| 173 | + 'height': height |
| 174 | + }; |
| 175 | + } |
| 176 | + } |
| 177 | + } |
| 178 | + } |
| 179 | + } |
| 180 | + |
| 181 | + function clearSelection() { |
| 182 | + ranges.$all.hide(); |
| 183 | + } |
| 184 | + |
| 185 | + function drawSelection( $container ) { |
| 186 | + if ( sel.from && sel.to ) { |
| 187 | + if ( sel.from.line === sel.to.line ) { |
| 188 | + // 1 line |
| 189 | + if ( sel.from.char !== sel.to.char ) { |
| 190 | + ranges.$first.show().css( { |
| 191 | + 'left': sel.from.x, |
| 192 | + 'top': sel.from.top, |
| 193 | + 'width': sel.to.x - sel.from.x, |
| 194 | + 'height': sel.from.height |
| 195 | + } ); |
| 196 | + ranges.$fill.hide(); |
| 197 | + ranges.$last.hide(); |
| 198 | + // XXX: Demo code! |
| 199 | + $( '#selection p' ).text( getSelectionText() ); |
| 200 | + return; |
| 201 | + } |
| 202 | + } else if ( sel.from.line < sel.to.line ) { |
| 203 | + // 2+ lines |
| 204 | + ranges.$first.show().css( { |
| 205 | + 'left': sel.from.x, |
| 206 | + 'top': sel.from.top, |
| 207 | + 'width': ( $container.innerWidth() - sel.from.x ) |
| 208 | + + $container.offset().left, |
| 209 | + 'height': sel.from.height |
| 210 | + } ); |
| 211 | + if ( sel.from.line < sel.to.line - 1 ) { |
| 212 | + ranges.$fill.show().css( { |
| 213 | + 'left': $container.offset().left, |
| 214 | + 'top': sel.from.bottom, |
| 215 | + 'width': $container.innerWidth(), |
| 216 | + 'height': sel.to.top - sel.from.bottom |
| 217 | + } ); |
| 218 | + } else { |
| 219 | + ranges.$fill.hide(); |
| 220 | + } |
| 221 | + ranges.$last.show().css( { |
| 222 | + 'left': $container.offset().left, |
| 223 | + 'top': sel.to.top, |
| 224 | + 'width': sel.to.x - $container.offset().left, |
| 225 | + 'height': sel.to.height |
| 226 | + } ); |
| 227 | + // XXX: Demo code! |
| 228 | + $( '#selection p' ).text( getSelectionText() ); |
| 229 | + return; |
| 230 | + } |
| 231 | + } |
| 232 | + // No selection |
| 233 | + ranges.$all.hide(); |
| 234 | + } |
| 235 | + |
| 236 | + function renderDocument( doc ) { |
| 237 | + $document.empty(); |
| 238 | + for ( var i = 0; i < doc.blocks.length; i++ ) { |
| 239 | + switch ( doc.blocks[i].type ) { |
| 240 | + case 'paragraph': |
| 241 | + renderParagraph( doc.blocks[i], $document ) |
| 242 | + break; |
| 243 | + } |
| 244 | + } |
| 245 | + } |
| 246 | + |
| 247 | + function renderParagraph( paragraph, $container ) { |
| 248 | + var $paragraph = $( '<div class="editSurface-paragraph"></div>' ).appendTo( $container ); |
| 249 | + var lines = []; |
| 250 | + for ( var i = 0; i < paragraph.lines.length; i++ ) { |
| 251 | + lines.push( paragraph.lines[i].text ); |
| 252 | + } |
| 253 | + $paragraph.flow( lines.join( '\n' ) ); |
| 254 | + } |
| 255 | + |
| 256 | + function update() { |
| 257 | + // Render document |
| 258 | + if ( options.document !== null ) { |
| 259 | + renderDocument( options.document, $this ); |
| 260 | + } |
| 261 | + } |
| 262 | + |
| 263 | + var cursor = { |
| 264 | + '$': $( '.editSurface-cursor' ), |
| 265 | + 'visible': true, |
| 266 | + 'timeout': null, |
| 267 | + 'speed': 500 |
| 268 | + }; |
| 269 | + cursor.blink = function() { |
| 270 | + // Flip |
| 271 | + cursor.visible = !cursor.visible; |
| 272 | + // Hide/show |
| 273 | + cursor.visible ? cursor.$.hide() : cursor.$.show(); |
| 274 | + // Repeat |
| 275 | + cursor.timeout = setTimeout( cursor.blink, cursor.speed ) |
| 276 | + } |
| 277 | + cursor.show = function() { |
| 278 | + // Start visible (will flip when run) |
| 279 | + cursor.visible = true; |
| 280 | + cursor.$.show(); |
| 281 | + // Start/restart blinking |
| 282 | + clearTimeout( cursor.timeout ); |
| 283 | + cursor.blink(); |
| 284 | + }; |
| 285 | + cursor.hide = function() { |
| 286 | + // Hide |
| 287 | + cursor.$.hide(); |
| 288 | + // Stop blinking |
| 289 | + clearTimeout( cursor.timeout ); |
| 290 | + }; |
| 291 | + |
| 292 | + $(window).resize( update ); |
| 293 | + update(); |
| 294 | +}; |
Property changes on: trunk/parsers/wikidom/demos/surface/jquery.editSurface.js |
___________________________________________________________________ |
Added: svn:eol-style |
1 | 295 | + native |
Added: svn:mime-type |
2 | 296 | + text/plain |
Index: trunk/parsers/wikidom/demos/surface/index.html |
— | — | @@ -3,7 +3,7 @@ |
4 | 4 | <html> |
5 | 5 | <head> |
6 | 6 | <title>EditSurface Demo</title> |
7 | | - <link rel="stylesheet" href="../../lib/jquery.editSurface.css"> |
| 7 | + <link rel="stylesheet" href="jquery.editSurface.css"> |
8 | 8 | </head> |
9 | 9 | <body> |
10 | 10 | <h1>EditSurface Demo</h1> |
— | — | @@ -12,8 +12,8 @@ |
13 | 13 | |
14 | 14 | <!-- EditSurface --> |
15 | 15 | <script type="text/javascript" src="../../lib/jquery.js"></script> |
16 | | - <script type="text/javascript" src="../../lib/jquery.flow.js"></script> |
17 | | - <script type="text/javascript" src="../../lib/jquery.editSurface.js"></script> |
| 16 | + <script type="text/javascript" src="jquery.flow.js"></script> |
| 17 | + <script type="text/javascript" src="jquery.editSurface.js"></script> |
18 | 18 | |
19 | 19 | <!-- Demo --> |
20 | 20 | <script> |