Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Newer
Older
100644 676 lines (534 sloc) 18.309 kB
7687b9c @fmarcia update comments and readme
authored
1 # Zen Coding for Gedit
abf67db @fmarcia introduce navigation through html nodes
authored
2 #
7687b9c @fmarcia update comments and readme
authored
3 # Copyright (C) 2010 Franck Marcia
abf67db @fmarcia introduce navigation through html nodes
authored
4 #
7687b9c @fmarcia update comments and readme
authored
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU General Public License as published by
7 # the Free Software Foundation, either version 3 of the License, or
8 # (at your option) any later version.
abf67db @fmarcia introduce navigation through html nodes
authored
9 #
7687b9c @fmarcia update comments and readme
authored
10 # This program is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # GNU General Public License for more details.
abf67db @fmarcia introduce navigation through html nodes
authored
14 #
7687b9c @fmarcia update comments and readme
authored
15 # You should have received a copy of the GNU General Public License
16 # along with this program. If not, see <http://www.gnu.org/licenses/>.
abf67db @fmarcia introduce navigation through html nodes
authored
17
18 import re
19
96a8f9c @fmarcia introduce zenify which reduces selection to abbreviation
authored
20 def factorize(zen_children):
21
22 zen_children = filter(lambda s: s, zen_children)
23
24 if len(zen_children) > 1:
25
26 zen_count = [1 for x in range(0, len(zen_children))]
27 index = 0
28 previous_child = ''
29
30 for zen_child in zen_children:
31 if zen_child == previous_child:
32 zen_count[index] = zen_count[index - 1] + 1
33 zen_count[index - 1] = 0
34 zen_children[index - 1] = ''
35 previous_child = zen_child
36 index += 1
37
38 index = 0
39 for zen_child in zen_children:
40 if zen_count[index] > 1:
41 parts = zen_child.split('>')
42 zen_children[index] = parts[0] + '*' + repr(zen_count[index])
43 if len(parts) > 1:
44 zen_children[index] += '>' + '>'.join(parts[1:])
45 index += 1
46
47 zen_children = filter(lambda s: s, zen_children)
48
49 return zen_children
50
51
abf67db @fmarcia introduce navigation through html nodes
authored
52 class Node ():
53
306b58b @fmarcia zenify handles root now
authored
54 tag_types_basic = ['root', 'tag', 'empty-tag']
ccfa2fa @fmarcia - introduce 'select next/previous tag'\n- 'select inward/outward' now…
authored
55 tag_types = ['tag', 'empty-tag', 'question-tag', 'exclam-tag', 'comment', 'cdata']
56 data_types = [ 'data', 'value' ]
57 other_types = [ 'root', 'attribute' ]
58
abf67db @fmarcia introduce navigation through html nodes
authored
59 def __init__(self, type, name = '', start = 0, end = 0, parent = None):
60 self.type = type
61 self.name = name
62 self.start = start
63 self.end = end
64 self.parent = parent
65 self.children = []
66
67 def __str__(self):
68 if self.name:
69 return '<%s:%s:%d:%d>' % (self.type, self.name, self.start, self.end)
70 else:
71 return '<%s:%d:%d>' % (self.type, self.start, self.end)
72
73 def append(self, type, start = 0, end = 0, name = ''):
74 child = Node(type, name, start, end, self)
75 self.children.append(child)
76 return child
77
78 def first_child(self):
79 if len(self.children) > 0:
80 return self.children[0]
81 return None
82
ccfa2fa @fmarcia - introduce 'select next/previous tag'\n- 'select inward/outward' now…
authored
83 def first_child_data(self):
abf67db @fmarcia introduce navigation through html nodes
authored
84 if len(self.children) > 0:
ccfa2fa @fmarcia - introduce 'select next/previous tag'\n- 'select inward/outward' now…
authored
85 for child in self.children:
86 if child.type == 'data':
87 return child
88 return None
89
96a8f9c @fmarcia introduce zenify which reduces selection to abbreviation
authored
90 def first_child_tag(self, basic = False):
91 tag_types = Node.tag_types_basic if basic else Node.tag_types
ccfa2fa @fmarcia - introduce 'select next/previous tag'\n- 'select inward/outward' now…
authored
92 if len(self.children) > 0:
93 for child in self.children:
96a8f9c @fmarcia introduce zenify which reduces selection to abbreviation
authored
94 if child.type in tag_types:
ccfa2fa @fmarcia - introduce 'select next/previous tag'\n- 'select inward/outward' now…
authored
95 return child
abf67db @fmarcia introduce navigation through html nodes
authored
96 return None
97
98 def next_sibling(self):
99 if self.parent:
100 siblings = self.parent.children
101 index = siblings.index(self)
102 if index < len(siblings) - 1:
103 return siblings[index + 1]
104 return None
105
96a8f9c @fmarcia introduce zenify which reduces selection to abbreviation
authored
106 def next_sibling_tag(self, basic = False):
107 tag_types = Node.tag_types_basic if basic else Node.tag_types
ccfa2fa @fmarcia - introduce 'select next/previous tag'\n- 'select inward/outward' now…
authored
108 if self.parent:
109 siblings = self.parent.children
110 index = siblings.index(self)
111 while index < len(siblings) - 1:
112 index += 1
96a8f9c @fmarcia introduce zenify which reduces selection to abbreviation
authored
113 if siblings[index].type in tag_types:
ccfa2fa @fmarcia - introduce 'select next/previous tag'\n- 'select inward/outward' now…
authored
114 return siblings[index]
115 return None
116
117 def last_child(self):
118 if len(self.children) > 0:
119 return self.children[-1]
120 return None
121
96a8f9c @fmarcia introduce zenify which reduces selection to abbreviation
authored
122 def last_child_tag(self, basic = False):
123 tag_types = Node.tag_types_basic if basic else Node.tag_types
ccfa2fa @fmarcia - introduce 'select next/previous tag'\n- 'select inward/outward' now…
authored
124 index = len(self.children)
125 while index > 0:
126 index -= 1
96a8f9c @fmarcia introduce zenify which reduces selection to abbreviation
authored
127 if self.children[index].type in tag_types:
ccfa2fa @fmarcia - introduce 'select next/previous tag'\n- 'select inward/outward' now…
authored
128 return self.children[index]
129 return None
130
abf67db @fmarcia introduce navigation through html nodes
authored
131 def previous_sibling(self):
132 if self.parent:
133 siblings = self.parent.children
134 index = siblings.index(self)
135 if index > 0:
136 return siblings[index - 1]
137 return None
138
96a8f9c @fmarcia introduce zenify which reduces selection to abbreviation
authored
139 def previous_sibling_tag(self, basic = False):
140 tag_types = Node.tag_types_basic if basic else Node.tag_types
ccfa2fa @fmarcia - introduce 'select next/previous tag'\n- 'select inward/outward' now…
authored
141 if self.parent:
142 siblings = self.parent.children
143 index = siblings.index(self)
144 while index > 0:
145 index -= 1
96a8f9c @fmarcia introduce zenify which reduces selection to abbreviation
authored
146 if siblings[index].type in tag_types:
ccfa2fa @fmarcia - introduce 'select next/previous tag'\n- 'select inward/outward' now…
authored
147 return siblings[index]
148 return None
149
96a8f9c @fmarcia introduce zenify which reduces selection to abbreviation
authored
150 def parent_tag(self, basic = False):
ccfa2fa @fmarcia - introduce 'select next/previous tag'\n- 'select inward/outward' now…
authored
151 test = self
96a8f9c @fmarcia introduce zenify which reduces selection to abbreviation
authored
152 tag_types = Node.tag_types_basic if basic else Node.tag_types
ccfa2fa @fmarcia - introduce 'select next/previous tag'\n- 'select inward/outward' now…
authored
153 while True:
154 test = test.parent
96a8f9c @fmarcia introduce zenify which reduces selection to abbreviation
authored
155 if test and test.type in tag_types:
ccfa2fa @fmarcia - introduce 'select next/previous tag'\n- 'select inward/outward' now…
authored
156 return test
157 return None
158
96a8f9c @fmarcia introduce zenify which reduces selection to abbreviation
authored
159 def is_child_of(self, node):
160 parent = self.parent
161 while parent:
162 if parent == node:
163 return True
164 parent = parent.parent
165 return False
166
167 def is_sibling_of(self, node):
168 if self.parent and self.parent.children:
169 children = self.parent.children
170 for child in children:
171 if child == node:
172 return True
173 return False
174
ccfa2fa @fmarcia - introduce 'select next/previous tag'\n- 'select inward/outward' now…
authored
175 def inner_bounds(self, offset_start, offset_end):
176
177 if self.type in ['data', 'attribute'] and self.parent:
178 return self.parent.inner_bounds(offset_start, offset_end)
179
180 elif self.type == 'value' and self.parent and self.parent.parent:
181 return self.parent.parent.inner_bounds(offset_start, offset_end)
182
183 elif self.type in ['empty-tag', 'question-tag', 'exclam-tag', 'comment', 'cdata']:
184 return self.start, self.end
185
186 elif self.type in ['root', 'tag'] and self.children:
187 node_start = self.first_child_data()
188 node_end = self.last_child()
189 if node_start and node_end:
190 if node_start.start == offset_start and node_end.end == offset_end:
191 first_tag = self.first_child_tag()
192 if first_tag:
193 return first_tag.start, first_tag.end
194 return node_start.start, node_end.end
195
196 return None, None
197
198 def outer_bounds(self, offset_start, offset_end):
199
200 if self.type in ['root', 'tag', 'empty-tag', 'question-tag', 'exclam-tag', 'comment', 'cdata']:
201
202 if offset_start == self.start and offset_end == self.end and self.parent:
203 start, end = self.parent.inner_bounds(offset_start, offset_end)
204
205 if start == offset_start and end == offset_end:
206 return self.parent.outer_bounds(offset_start, offset_end)
207 else:
208 return start, end
209
210 return self.start, self.end
211
212 elif self.type in ['data', 'attribute'] and self.parent:
213 return self.parent.outer_bounds(offset_start, offset_end)
214
215 elif self.type == 'value' and self.parent and self.parent.parent:
216 return self.parent.parent.outer_bounds(offset_start, offset_end)
217
218 return None, None
219
f2c57ac @fmarcia zenify allows 4 different modes
authored
220 def zenify(self, content, mode):
96a8f9c @fmarcia introduce zenify which reduces selection to abbreviation
authored
221
222 if self.type not in Node.tag_types_basic:
223 return ''
224
225 zen_id = ''
226 zen_class = ''
227 zen_children = []
f2c57ac @fmarcia zenify allows 4 different modes
authored
228 zen_attributes = []
229
96a8f9c @fmarcia introduce zenify which reduces selection to abbreviation
authored
230 for child in self.children:
f2c57ac @fmarcia zenify allows 4 different modes
authored
231
96a8f9c @fmarcia introduce zenify which reduces selection to abbreviation
authored
232 name = child.name.lower()
f2c57ac @fmarcia zenify allows 4 different modes
authored
233
234 if mode > 0 and child.type == 'attribute' and name in ['id', 'class']:
96a8f9c @fmarcia introduce zenify which reduces selection to abbreviation
authored
235 for grand_child in child.children:
236 if grand_child.type == 'value' and grand_child.start < grand_child.end and not grand_child.children:
237 if name == 'id':
3aca125 @fmarcia fix zenify: handle tabs and newlines in ids and classes
authored
238 zen_id = '#' + '#'.join(filter(lambda s: s, re.split('\s+', content[grand_child.start:grand_child.end])))
96a8f9c @fmarcia introduce zenify which reduces selection to abbreviation
authored
239 elif name == 'class':
3aca125 @fmarcia fix zenify: handle tabs and newlines in ids and classes
authored
240 zen_class = '.' + '.'.join(filter(lambda s: s, re.split('\s+', content[grand_child.start:grand_child.end])))
96a8f9c @fmarcia introduce zenify which reduces selection to abbreviation
authored
241
f2c57ac @fmarcia zenify allows 4 different modes
authored
242 elif mode > 1 and child.type == 'attribute':
243 for grand_child in child.children:
244 if grand_child.type == 'value' and grand_child.start < grand_child.end and not grand_child.children:
245 if mode == 2 or name.startswith('on'):
246 zen_attributes.append(name)
247 else:
248 zen_attributes.append(name + '="' + content[grand_child.start:grand_child.end] + '"')
96a8f9c @fmarcia introduce zenify which reduces selection to abbreviation
authored
249
f2c57ac @fmarcia zenify allows 4 different modes
authored
250 elif child.type in Node.tag_types_basic:
251 zen_children.append(child.zenify(content, mode))
96a8f9c @fmarcia introduce zenify which reduces selection to abbreviation
authored
252
f2c57ac @fmarcia zenify allows 4 different modes
authored
253 zen_children = factorize(zen_children)
96a8f9c @fmarcia introduce zenify which reduces selection to abbreviation
authored
254 if len(zen_children) == 0:
255 zen_children_string = ''
256 elif len(zen_children) == 1:
257 zen_children_string = '>' + zen_children[0]
258 else:
259 zen_children_string = '>(' + '+'.join(zen_children) + ')'
260
f2c57ac @fmarcia zenify allows 4 different modes
authored
261 zen_attributes_string = ''
262 if zen_attributes:
263 zen_attributes_string = '[' + ' '.join(zen_attributes) + ']'
264
96a8f9c @fmarcia introduce zenify which reduces selection to abbreviation
authored
265 if zen_children_string:
f2c57ac @fmarcia zenify allows 4 different modes
authored
266 return '(' + self.name + zen_id + zen_class + zen_attributes_string + zen_children_string + ')'
96a8f9c @fmarcia introduce zenify which reduces selection to abbreviation
authored
267 else:
f2c57ac @fmarcia zenify allows 4 different modes
authored
268 return self.name + zen_id + zen_class + zen_attributes_string
96a8f9c @fmarcia introduce zenify which reduces selection to abbreviation
authored
269
abf67db @fmarcia introduce navigation through html nodes
authored
270 def show(self, level = 0):
271 children = ''
272 for child in self.children:
273 children += child.show(level + 1)
274 return ('\t' * level) + str(self) + '\n' + children
275
276
277 class HtmlNavigation():
278
279 def __init__(self, content):
280 self.content = content
281 self.tree = self._parse(content)
282
283 def _parse(self, content, print_and_exit = False):
284
285 empty_tags = ['area','base','basefont','br','col','embed','frame','hr','img','input','isindex','link','meta','param']
286
287 def tokens_feed():
288 tokens_re = '(<!--|-->|<\!\[CDATA\[|\]\]>|<\?|\?>|<\!|<[A-Za-z][A-Za-z0-9\-_:\.]*|</[A-Za-z][A-Za-z0-9\-_:\.]*\s*>|/?>|["\'=]|\s+)'
289 tokens = filter(lambda s: s, re.split(tokens_re, content))
290 for token in tokens:
291 yield token
292
293 def get_next_token():
294 try:
295 return tokens.next()
296 except StopIteration:
297 return None
298
299 tokens = tokens_feed()
300
301 if print_and_exit:
302 for token in tokens: print token, '|',
303 print
304 return None
305
306 root = Node('root')
ccfa2fa @fmarcia - introduce 'select next/previous tag'\n- 'select inward/outward' now…
authored
307 node = root.append('data')
abf67db @fmarcia introduce navigation through html nodes
authored
308 offset = 0
309 end = 0
310 token = ''
311
312 while True:
313
314 previous_token = token
315 offset = end
316
317 token = get_next_token()
318 if not token: break
319 end = offset + len(token)
320
321 if token == '<!--':
ccfa2fa @fmarcia - introduce 'select next/previous tag'\n- 'select inward/outward' now…
authored
322 if node.type == 'data':
abf67db @fmarcia introduce navigation through html nodes
authored
323 node.end = offset
324 node = node.parent
325 node = node.append('comment', offset)
326
327 elif token == '-->':
328 if node.type == 'comment':
329 node.end = end
ccfa2fa @fmarcia - introduce 'select next/previous tag'\n- 'select inward/outward' now…
authored
330 node = node.parent.append('data', end)
abf67db @fmarcia introduce navigation through html nodes
authored
331
332 elif token == '<![CDATA[':
ccfa2fa @fmarcia - introduce 'select next/previous tag'\n- 'select inward/outward' now…
authored
333 if node.type == 'data':
abf67db @fmarcia introduce navigation through html nodes
authored
334 node.end = offset
335 node = node.parent
336 node = node.append('cdata', offset)
337
338 elif token == ']]>':
339 if node.type == 'cdata':
340 node.end = end
ccfa2fa @fmarcia - introduce 'select next/previous tag'\n- 'select inward/outward' now…
authored
341 node = node.parent.append('data', end)
abf67db @fmarcia introduce navigation through html nodes
authored
342
343 elif token == '<?':
344 if node.type != 'question--tag':
ccfa2fa @fmarcia - introduce 'select next/previous tag'\n- 'select inward/outward' now…
authored
345 if node.type == 'data':
abf67db @fmarcia introduce navigation through html nodes
authored
346 node.end = offset
347 node = node.parent
348 node = node.append('question-tag', offset)
349
350 elif token == '?>':
351 if node.type == 'question-tag':
352 node.end = end
353 previous_sibling = node.previous_sibling()
ccfa2fa @fmarcia - introduce 'select next/previous tag'\n- 'select inward/outward' now…
authored
354 if previous_sibling and previous_sibling.type == 'data':
355 node = node.parent.append('data', end)
abf67db @fmarcia introduce navigation through html nodes
authored
356 else:
357 node = node.parent
358
359 elif token == '<!':
ccfa2fa @fmarcia - introduce 'select next/previous tag'\n- 'select inward/outward' now…
authored
360 if node.type == 'data':
abf67db @fmarcia introduce navigation through html nodes
authored
361 node.end = offset
362 node = node.parent.append('exclam-tag', offset)
363
364 elif token.startswith('</'):
365
efb77ae @fmarcia fix a bug when tags are present in a script
authored
366 if node.type == 'script-data':
367
368 name = token[2:-1].rstrip().lower()
369 if name == 'script':
370 node.type = 'data'
371 node.end = offset
372 node = node.parent
373 node.end = end
374 node = node.parent.append('data', end)
375
376 elif node.type == 'data':
abf67db @fmarcia introduce navigation through html nodes
authored
377
378 node.end = offset
379
380 name = token[2:-1].rstrip().lower()
381 current_node = node
382 node = node.parent
383
384 while True:
385 if node.type == 'tag':
386 if node.name == name:
387 node.end = end
ccfa2fa @fmarcia - introduce 'select next/previous tag'\n- 'select inward/outward' now…
authored
388 node = node.parent.append('data', end)
abf67db @fmarcia introduce navigation through html nodes
authored
389 break
390 else:
391 node.end = end
392 node = node.parent
393 if node.type == 'root':
394 node = current_node
395 break
396 else:
397 break
398
399 elif token.startswith('<'):
400 name = token[1:].rstrip().lower()
ccfa2fa @fmarcia - introduce 'select next/previous tag'\n- 'select inward/outward' now…
authored
401 if node.type == 'data' and name:
abf67db @fmarcia introduce navigation through html nodes
authored
402 node.end = offset
403 node = node.parent.append('start-tag', offset, 0, name)
404
405 elif token == '/>':
ccfa2fa @fmarcia - introduce 'select next/previous tag'\n- 'select inward/outward' now…
authored
406 if node.type in ['start-tag', 'attribute']:
407 if node.type == 'attribute':
408 node.end = offset
409 node = node.parent
abf67db @fmarcia introduce navigation through html nodes
authored
410 node.type = 'empty-tag'
411 node.end = end
ccfa2fa @fmarcia - introduce 'select next/previous tag'\n- 'select inward/outward' now…
authored
412 node = node.parent.append('data', end)
abf67db @fmarcia introduce navigation through html nodes
authored
413
414 elif token == '>':
415 if node.type == 'exclam-tag':
416 node.end = end
ccfa2fa @fmarcia - introduce 'select next/previous tag'\n- 'select inward/outward' now…
authored
417 node = node.parent.append('data', end)
abf67db @fmarcia introduce navigation through html nodes
authored
418 elif node.type in ['start-tag', 'attribute']:
419 if node.type == 'attribute':
420 node.end = offset
421 node = node.parent
422 if node.name in empty_tags:
423 node.type = 'empty-tag'
424 node.end = end
ccfa2fa @fmarcia - introduce 'select next/previous tag'\n- 'select inward/outward' now…
authored
425 node = node.parent.append('data', end)
abf67db @fmarcia introduce navigation through html nodes
authored
426 else:
427 node.type = 'tag'
efb77ae @fmarcia fix a bug when tags are present in a script
authored
428 if node.name == 'script':
429 node = node.append('script-data', end)
430 else:
431 node = node.append('data', end)
abf67db @fmarcia introduce navigation through html nodes
authored
432
433 elif token == '"':
434 if node.type == 'attribute' and previous_token == '=':
ccfa2fa @fmarcia - introduce 'select next/previous tag'\n- 'select inward/outward' now…
authored
435 node = node.append('double-quoted-value', end)
436 elif node.type == 'double-quoted-value':
437 node.type = 'value'
abf67db @fmarcia introduce navigation through html nodes
authored
438 node.end = offset
439 node = node.parent
440 node.end = end
441 node = node.parent
442
443 elif token == "'":
444 if node.type == 'attribute' and previous_token == '=':
ccfa2fa @fmarcia - introduce 'select next/previous tag'\n- 'select inward/outward' now…
authored
445 node = node.append('single-quoted-value', end)
446 elif node.type == 'single-quoted-value':
447 node.type = 'value'
abf67db @fmarcia introduce navigation through html nodes
authored
448 node.end = offset
449 node = node.parent
450 node.end = end
451 node = node.parent
452
453 else:
454 is_alnum = not re.sub('[A-Za-z][A-Za-z0-9\-_:\.]*', '', token)
455 if node.type == 'start-tag':
456 if is_alnum:
457 node = node.append('attribute', offset, 0, token)
458
459 elif node.type == 'attribute':
460 if previous_token == '=' and is_alnum:
ccfa2fa @fmarcia - introduce 'select next/previous tag'\n- 'select inward/outward' now…
authored
461 node.append('value', offset, end)
abf67db @fmarcia introduce navigation through html nodes
authored
462 node.end = end
463 node = node.parent
464 elif token != '=':
465 node.end = offset
466 node = node.parent
467
468 while node.type != 'root':
469 node.end = end
470 node = node.parent
471 node.end = end
472
473 return root
474
475 def current(self, offset_start, offset_end, tree = None):
476
477 if tree is None:
478 tree = self.tree
479
480 result = tree
481 for child in tree.children:
482
483 if child.start <= offset_start and offset_end <= child.end:
484 sibling = child.next_sibling()
485
486 if sibling and sibling.start == offset_start and sibling.end == offset_end:
487 return sibling
488
489 sibling = child.previous_sibling()
490
491 if sibling and sibling.start == offset_start and sibling.end == offset_end:
492 return sibling
493
494 result = self.current(offset_start, offset_end, child)
495
496 return result
497
498 def _prepare(self, offset_start, offset_end, content):
499 if content != self.content:
500 self.__init__(content)
501 return self.current(offset_start, offset_end)
502
ccfa2fa @fmarcia - introduce 'select next/previous tag'\n- 'select inward/outward' now…
authored
503 def previous_node(self, offset_start, offset_end, content):
abf67db @fmarcia introduce navigation through html nodes
authored
504
505 result = None
506 current = self._prepare(offset_start, offset_end, content)
507
508 if current:
509 result = current.previous_sibling()
510
511 if result:
512 test = result.last_child()
513
514 while test:
515 result = test
516 test = test.last_child()
517 else:
518 result = current.parent
519
520 if result:
521 test = result.last_child()
522
523 while test and test.start < offset_start:
524 result = test
525 test = test.last_child()
ccfa2fa @fmarcia - introduce 'select next/previous tag'\n- 'select inward/outward' now…
authored
526
abf67db @fmarcia introduce navigation through html nodes
authored
527 return result
528
ccfa2fa @fmarcia - introduce 'select next/previous tag'\n- 'select inward/outward' now…
authored
529 def next_node(self, offset_start, offset_end, content):
abf67db @fmarcia introduce navigation through html nodes
authored
530
531 current = self._prepare(offset_start, offset_end, content)
532 if not current:
533 return None
534
535 test = current.first_child()
536 if test:
537 return test
538
539 test = current.next_sibling()
540 if test:
541 return test
542
543 while True:
544 current = current.parent
545 if not current:
546 return None
547 test = current.next_sibling()
548 if test:
549 return test
550
ccfa2fa @fmarcia - introduce 'select next/previous tag'\n- 'select inward/outward' now…
authored
551 def previous_tag(self, offset_start, offset_end, content):
552
553 result = None
554 current = self._prepare(offset_start, offset_end, content)
555
556 if current:
557 result = current.previous_sibling_tag()
558
559 if result:
560 test = result.last_child_tag()
561
562 while test:
563 result = test
564 test = test.last_child_tag()
565 else:
566 result = current.parent_tag()
567
568 if result:
569 test = result.last_child_tag()
570
571 while test and test.start < offset_start:
572 result = test
573 test = test.last_child_tag()
574
575 return result
576
577 def next_tag(self, offset_start, offset_end, content):
578
579 current = self._prepare(offset_start, offset_end, content)
580 if not current:
581 return None
582
583 test = current.first_child_tag()
584 if test:
585 return test
586
587 test = current.next_sibling_tag()
588 if test:
589 return test
590
591 while True:
592 current = current.parent
593 if not current:
594 return None
595 test = current.next_sibling_tag()
596 if test:
597 return test
598
599 def inner_bounds(self, offset_start, offset_end, content):
600 current = self._prepare(offset_start, offset_end, content)
601 if current:
602 return current.inner_bounds(offset_start, offset_end)
603 return None, None
604
605 def outer_bounds(self, offset_start, offset_end, content):
606 current = self._prepare(offset_start, offset_end, content)
607 if current:
608 return current.outer_bounds(offset_start, offset_end)
609 return None, None
610
f2c57ac @fmarcia zenify allows 4 different modes
authored
611 def zenify(self, offset_start, offset_end, content, mode = 3):
96a8f9c @fmarcia introduce zenify which reduces selection to abbreviation
authored
612
613 current_start = self._prepare(offset_start, offset_start, content)
614 while current_start.type not in Node.tag_types_basic:
615 if current_start.parent:
616 current_start = current_start.parent
617 else:
618 current_start = self._prepare(0, 0, content)
619 break
620
621 if current_start.end == offset_end:
622 current_end = current_start
623
624 else:
625 current_end = self._prepare(offset_end, offset_end, content)
626 while current_end.type not in Node.tag_types_basic:
627 if current_end.parent:
628 current_end = current_end.parent
629 else:
630 current_end = self._prepare(len(content) - 1, len(content) - 1, content)
631 break
306b58b @fmarcia zenify handles root now
authored
632
96a8f9c @fmarcia introduce zenify which reduces selection to abbreviation
authored
633 nodes = []
306b58b @fmarcia zenify handles root now
authored
634
635 if current_start.type == 'root':
636 for child in current_start.children:
637 if current_start.type in Node.tag_types_basic:
638 nodes.append(child)
639
640 elif current_end.is_child_of(current_start) or current_start == current_end:
96a8f9c @fmarcia introduce zenify which reduces selection to abbreviation
authored
641 nodes.append(current_start)
306b58b @fmarcia zenify handles root now
authored
642
96a8f9c @fmarcia introduce zenify which reduces selection to abbreviation
authored
643 elif current_end.is_sibling_of(current_start):
644 while current_start and current_start != current_end:
645 if current_start.type in Node.tag_types_basic:
646 nodes.append(current_start)
647 current_start = current_start.next_sibling_tag(True)
648 nodes.append(current_end)
306b58b @fmarcia zenify handles root now
authored
649
96a8f9c @fmarcia introduce zenify which reduces selection to abbreviation
authored
650 else:
651 offset_start, offset_end = self.outer_bounds(offset_start, offset_end, content)
652 nodes.append(self._prepare(offset_start, offset_end, content))
653
654 result = []
655 for node in nodes:
f2c57ac @fmarcia zenify allows 4 different modes
authored
656 result.append(node.zenify(content, mode))
96a8f9c @fmarcia introduce zenify which reduces selection to abbreviation
authored
657 result = factorize(result)
658 return '+'.join(result)
659
660
abf67db @fmarcia introduce navigation through html nodes
authored
661 if __name__ == '__main__':
662
663 content = '''
ccfa2fa @fmarcia - introduce 'select next/previous tag'\n- 'select inward/outward' now…
authored
664 <div id="id<?=$var_id?>" <?=$attr?>> <
abf67db @fmarcia introduce navigation through html nodes
authored
665 ><div class="tab media-genre tab-init" id="tab_media" onclick="toggleTab('media');">Mo<b>v<i>i</b>e</i></div>
666 <span id="txt_current_date" style="font-weight:bold;font-size:16px;color:#636363;">Monday, May 17th< 2010</span>
667 &nbsp;<a class="link" style="cursor: pointer;" onclick="saveGrid();">Start h>ere</a>
668 &nbsp;<a class="link" href="/grid.php?defaut=yes">Default grid</a>
669 </div><!-- test -->
670 '''
671 test = HtmlNavigation(content)
672 if test.tree:
673 print test.tree.show()
674 test._parse(content, True)
675
Something went wrong with that request. Please try again.