pyDiffTools 0.1.31__tar.gz → 0.1.33__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {pydifftools-0.1.31 → pydifftools-0.1.33}/MANIFEST.in +2 -0
- {pydifftools-0.1.31/pyDiffTools.egg-info → pydifftools-0.1.33}/PKG-INFO +1 -1
- {pydifftools-0.1.31 → pydifftools-0.1.33/pyDiffTools.egg-info}/PKG-INFO +1 -1
- {pydifftools-0.1.31 → pydifftools-0.1.33}/pyDiffTools.egg-info/SOURCES.txt +2 -0
- pydifftools-0.1.33/pydifftools/doc_contents.py +206 -0
- {pydifftools-0.1.31 → pydifftools-0.1.33}/pydifftools/flowchart/comparison.py +3 -4
- {pydifftools-0.1.31 → pydifftools-0.1.33}/pydifftools/flowchart/graph.py +4 -1
- pydifftools-0.1.33/pydifftools/flowchart/svg-pan-zoom.min.js +30 -0
- {pydifftools-0.1.31 → pydifftools-0.1.33}/pydifftools/flowchart/watch_graph.py +38 -7
- pydifftools-0.1.33/pydifftools/flowchart/wgrph_view.js +212 -0
- {pydifftools-0.1.31 → pydifftools-0.1.33}/pydifftools/outline.py +2 -2
- {pydifftools-0.1.31 → pydifftools-0.1.33}/pyproject.toml +5 -1
- pydifftools-0.1.31/pydifftools/doc_contents.py +0 -162
- {pydifftools-0.1.31 → pydifftools-0.1.33}/LICENSE.md +0 -0
- {pydifftools-0.1.31 → pydifftools-0.1.33}/README.rst +0 -0
- {pydifftools-0.1.31 → pydifftools-0.1.33}/example_notebook/_quarto.yml +0 -0
- {pydifftools-0.1.31 → pydifftools-0.1.33}/example_notebook/project1/.jupyter_cache/__version__.txt +0 -0
- {pydifftools-0.1.31 → pydifftools-0.1.33}/example_notebook/project1/.jupyter_cache/executed/1a724af72b16f5a9e607e12b1c721645/base.ipynb +0 -0
- {pydifftools-0.1.31 → pydifftools-0.1.33}/example_notebook/project1/.jupyter_cache/executed/1b28fc9daac9081847e5161b2c546f8a/base.ipynb +0 -0
- {pydifftools-0.1.31 → pydifftools-0.1.33}/example_notebook/project1/.jupyter_cache/executed/231f64eee282fa225d1104935cf80a24/base.ipynb +0 -0
- {pydifftools-0.1.31 → pydifftools-0.1.33}/example_notebook/project1/.jupyter_cache/executed/26e56f6b0ff54851a45145157f2f0dc4/base.ipynb +0 -0
- {pydifftools-0.1.31 → pydifftools-0.1.33}/example_notebook/project1/.jupyter_cache/executed/311fabd7029ffd050d056e2f316eb50f/base.ipynb +0 -0
- {pydifftools-0.1.31 → pydifftools-0.1.33}/example_notebook/project1/.jupyter_cache/executed/57da2021e5b156ac3adf01398201c723/base.ipynb +0 -0
- {pydifftools-0.1.31 → pydifftools-0.1.33}/example_notebook/project1/.jupyter_cache/executed/62b24ea7da75011d92b0f8924faa208d/base.ipynb +0 -0
- {pydifftools-0.1.31 → pydifftools-0.1.33}/example_notebook/project1/.jupyter_cache/executed/7f1b20d69d889514ab5d1cc92e3cb14f/base.ipynb +0 -0
- {pydifftools-0.1.31 → pydifftools-0.1.33}/example_notebook/project1/.jupyter_cache/executed/86f74c8c54a87ff892d9b15dd714e8f0/base.ipynb +0 -0
- {pydifftools-0.1.31 → pydifftools-0.1.33}/example_notebook/project1/.jupyter_cache/executed/88893b4234eac2945d9d6cb2e277f186/base.ipynb +0 -0
- {pydifftools-0.1.31 → pydifftools-0.1.33}/example_notebook/project1/.jupyter_cache/executed/9a40046ada6f582ee34af00fbdbfb417/base.ipynb +0 -0
- {pydifftools-0.1.31 → pydifftools-0.1.33}/example_notebook/project1/.jupyter_cache/executed/a1bf4d270d0641ff41faf1d7cce3439a/base.ipynb +0 -0
- {pydifftools-0.1.31 → pydifftools-0.1.33}/example_notebook/project1/.jupyter_cache/executed/a3789f7d9585a781f2a1c60ce95ff10d/base.ipynb +0 -0
- {pydifftools-0.1.31 → pydifftools-0.1.33}/example_notebook/project1/.jupyter_cache/executed/be46ecba858d39ad5f0c46902ddf1c02/base.ipynb +0 -0
- {pydifftools-0.1.31 → pydifftools-0.1.33}/example_notebook/project1/.jupyter_cache/executed/d0cbc57a12f2ccce710a5afd04cc05e7/base.ipynb +0 -0
- {pydifftools-0.1.31 → pydifftools-0.1.33}/example_notebook/project1/.jupyter_cache/executed/d3e12d320b14228f701231ae32ddd7dd/base.ipynb +0 -0
- {pydifftools-0.1.31 → pydifftools-0.1.33}/example_notebook/project1/.jupyter_cache/executed/eb434f61555438d020a6970a5dbf9ee8/base.ipynb +0 -0
- {pydifftools-0.1.31 → pydifftools-0.1.33}/example_notebook/project1/.jupyter_cache/executed/f6b0e73aa7fa029134665d4dde57e096/base.ipynb +0 -0
- {pydifftools-0.1.31 → pydifftools-0.1.33}/example_notebook/project1/.jupyter_cache/executed/f719a6e4ff09873cb0ffb06ec9d232f9/base.ipynb +0 -0
- {pydifftools-0.1.31 → pydifftools-0.1.33}/example_notebook/project1/.jupyter_cache/executed/feeee244a7ce3d60e1a227eb604df823/base.ipynb +0 -0
- {pydifftools-0.1.31 → pydifftools-0.1.33}/example_notebook/project1/.jupyter_cache/global.db +0 -0
- {pydifftools-0.1.31 → pydifftools-0.1.33}/example_notebook/project1/example.qmd +0 -0
- {pydifftools-0.1.31 → pydifftools-0.1.33}/example_notebook/project1/example.tex +0 -0
- {pydifftools-0.1.31 → pydifftools-0.1.33}/example_notebook/project1/index.qmd +0 -0
- {pydifftools-0.1.31 → pydifftools-0.1.33}/example_notebook/project1/subproject1/.jupyter_cache/__version__.txt +0 -0
- {pydifftools-0.1.31 → pydifftools-0.1.33}/example_notebook/project1/subproject1/.jupyter_cache/executed/ca90e4df5f4f0583df6554156a68dc7f/base.ipynb +0 -0
- {pydifftools-0.1.31 → pydifftools-0.1.33}/example_notebook/project1/subproject1/.jupyter_cache/global.db +0 -0
- {pydifftools-0.1.31 → pydifftools-0.1.33}/example_notebook/project1/subproject1/end_vs_he_sketch.jpg +0 -0
- {pydifftools-0.1.31 → pydifftools-0.1.33}/example_notebook/project1/subproject1/independent.qmd +0 -0
- {pydifftools-0.1.31 → pydifftools-0.1.33}/example_notebook/project1/subproject1/index.qmd +0 -0
- {pydifftools-0.1.31 → pydifftools-0.1.33}/example_notebook/project1/subproject1/tasks.qmd +0 -0
- {pydifftools-0.1.31 → pydifftools-0.1.33}/example_notebook/project1/subproject1/test_include.qmd +0 -0
- {pydifftools-0.1.31 → pydifftools-0.1.33}/example_notebook/project1/subproject1/tryforerror.qmd +0 -0
- {pydifftools-0.1.31 → pydifftools-0.1.33}/example_notebook/project1/test_include.qmd +0 -0
- {pydifftools-0.1.31 → pydifftools-0.1.33}/pyDiffTools.egg-info/dependency_links.txt +0 -0
- {pydifftools-0.1.31 → pydifftools-0.1.33}/pyDiffTools.egg-info/entry_points.txt +0 -0
- {pydifftools-0.1.31 → pydifftools-0.1.33}/pyDiffTools.egg-info/requires.txt +0 -0
- {pydifftools-0.1.31 → pydifftools-0.1.33}/pyDiffTools.egg-info/top_level.txt +0 -0
- {pydifftools-0.1.31 → pydifftools-0.1.33}/pydifftools/__init__.py +0 -0
- {pydifftools-0.1.31 → pydifftools-0.1.33}/pydifftools/browser_lifecycle.py +0 -0
- {pydifftools-0.1.31 → pydifftools-0.1.33}/pydifftools/check_numbers.py +0 -0
- {pydifftools-0.1.31 → pydifftools-0.1.33}/pydifftools/command_line.py +0 -0
- {pydifftools-0.1.31 → pydifftools-0.1.33}/pydifftools/command_registry.py +0 -0
- {pydifftools-0.1.31 → pydifftools-0.1.33}/pydifftools/comment_functions.py +0 -0
- {pydifftools-0.1.31 → pydifftools-0.1.33}/pydifftools/comment_tags.lua +0 -0
- {pydifftools-0.1.31 → pydifftools-0.1.33}/pydifftools/comment_tags_margin.lua +0 -0
- {pydifftools-0.1.31 → pydifftools-0.1.33}/pydifftools/comment_tags_no_comments.lua +0 -0
- {pydifftools-0.1.31 → pydifftools-0.1.33}/pydifftools/comment_toggle.js +0 -0
- {pydifftools-0.1.31 → pydifftools-0.1.33}/pydifftools/comments.css +0 -0
- {pydifftools-0.1.31 → pydifftools-0.1.33}/pydifftools/continuous.py +0 -0
- {pydifftools-0.1.31 → pydifftools-0.1.33}/pydifftools/copy_files.py +0 -0
- {pydifftools-0.1.31 → pydifftools-0.1.33}/pydifftools/diff-doc.js +0 -0
- {pydifftools-0.1.31 → pydifftools-0.1.33}/pydifftools/flowchart/__init__.py +0 -0
- {pydifftools-0.1.31 → pydifftools-0.1.33}/pydifftools/flowchart/dot_to_yaml.py +0 -0
- {pydifftools-0.1.31 → pydifftools-0.1.33}/pydifftools/forward_search.py +0 -0
- {pydifftools-0.1.31 → pydifftools-0.1.33}/pydifftools/git_gd.py +0 -0
- {pydifftools-0.1.31 → pydifftools-0.1.33}/pydifftools/git_gd_history.py +0 -0
- {pydifftools-0.1.31 → pydifftools-0.1.33}/pydifftools/git_gd_image.py +0 -0
- {pydifftools-0.1.31 → pydifftools-0.1.33}/pydifftools/git_gd_qt.py +0 -0
- {pydifftools-0.1.31 → pydifftools-0.1.33}/pydifftools/html_comments.py +0 -0
- {pydifftools-0.1.31 → pydifftools-0.1.33}/pydifftools/html_uncomments.py +0 -0
- {pydifftools-0.1.31 → pydifftools-0.1.33}/pydifftools/log_example.py +0 -0
- {pydifftools-0.1.31 → pydifftools-0.1.33}/pydifftools/match_spaces.py +0 -0
- {pydifftools-0.1.31 → pydifftools-0.1.33}/pydifftools/notebook/__init__.py +0 -0
- {pydifftools-0.1.31 → pydifftools-0.1.33}/pydifftools/notebook/display.py +0 -0
- {pydifftools-0.1.31 → pydifftools-0.1.33}/pydifftools/notebook/fast_build.py +0 -0
- {pydifftools-0.1.31 → pydifftools-0.1.33}/pydifftools/notebook/tex_to_qmd.py +0 -0
- {pydifftools-0.1.31 → pydifftools-0.1.33}/pydifftools/onewordify.py +0 -0
- {pydifftools-0.1.31 → pydifftools-0.1.33}/pydifftools/onewordify_undo.py +0 -0
- {pydifftools-0.1.31 → pydifftools-0.1.33}/pydifftools/rearrange_tex.py +0 -0
- {pydifftools-0.1.31 → pydifftools-0.1.33}/pydifftools/searchacro.py +0 -0
- {pydifftools-0.1.31 → pydifftools-0.1.33}/pydifftools/separate_comments.py +0 -0
- {pydifftools-0.1.31 → pydifftools-0.1.33}/pydifftools/split_conflict.py +0 -0
- {pydifftools-0.1.31 → pydifftools-0.1.33}/pydifftools/unseparate_comments.py +0 -0
- {pydifftools-0.1.31 → pydifftools-0.1.33}/pydifftools/update_check.py +0 -0
- {pydifftools-0.1.31 → pydifftools-0.1.33}/pydifftools/wrap_sentences.py +0 -0
- {pydifftools-0.1.31 → pydifftools-0.1.33}/pydifftools/xml2xlsx.vbs +0 -0
- {pydifftools-0.1.31 → pydifftools-0.1.33}/setup.cfg +0 -0
- {pydifftools-0.1.31 → pydifftools-0.1.33}/tests/test_browser_lifecycle.py +0 -0
- {pydifftools-0.1.31 → pydifftools-0.1.33}/tests/test_check_single_use.py +0 -0
- {pydifftools-0.1.31 → pydifftools-0.1.33}/tests/test_command_line_help.py +0 -0
- {pydifftools-0.1.31 → pydifftools-0.1.33}/tests/test_continuous_shutdown.py +0 -0
- {pydifftools-0.1.31 → pydifftools-0.1.33}/tests/test_forward_search.py +0 -0
- {pydifftools-0.1.31 → pydifftools-0.1.33}/tests/test_git_gd_history.py +0 -0
- {pydifftools-0.1.31 → pydifftools-0.1.33}/tests/test_git_gd_image.py +0 -0
- {pydifftools-0.1.31 → pydifftools-0.1.33}/tests/test_git_gd_qt.py +0 -0
- {pydifftools-0.1.31 → pydifftools-0.1.33}/tests/test_rrng.py +0 -0
- {pydifftools-0.1.31 → pydifftools-0.1.33}/tests/test_tex_to_qmd.py +0 -0
- {pydifftools-0.1.31 → pydifftools-0.1.33}/tests/test_update_check.py +0 -0
- {pydifftools-0.1.31 → pydifftools-0.1.33}/tests/test_wrap_check.py +0 -0
- {pydifftools-0.1.31 → pydifftools-0.1.33}/tests/test_wrapping.py +0 -0
|
@@ -8,4 +8,6 @@ include pydifftools/comment_tags.lua
|
|
|
8
8
|
include pydifftools/comment_tags_margin.lua
|
|
9
9
|
include pydifftools/comment_tags_no_comments.lua
|
|
10
10
|
include pydifftools/comment_toggle.js
|
|
11
|
+
include pydifftools/flowchart/svg-pan-zoom.min.js
|
|
12
|
+
include pydifftools/flowchart/wgrph_view.js
|
|
11
13
|
recursive-include example_notebook *
|
|
@@ -83,7 +83,9 @@ pydifftools/flowchart/__init__.py
|
|
|
83
83
|
pydifftools/flowchart/comparison.py
|
|
84
84
|
pydifftools/flowchart/dot_to_yaml.py
|
|
85
85
|
pydifftools/flowchart/graph.py
|
|
86
|
+
pydifftools/flowchart/svg-pan-zoom.min.js
|
|
86
87
|
pydifftools/flowchart/watch_graph.py
|
|
88
|
+
pydifftools/flowchart/wgrph_view.js
|
|
87
89
|
pydifftools/notebook/__init__.py
|
|
88
90
|
pydifftools/notebook/display.py
|
|
89
91
|
pydifftools/notebook/fast_build.py
|
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
from collections import OrderedDict
|
|
2
|
+
import re
|
|
3
|
+
from fuzzywuzzy import fuzz
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class doc_contents_class(object):
|
|
7
|
+
def __init__(self, format_type="latex"):
|
|
8
|
+
self.contents = OrderedDict()
|
|
9
|
+
self.contents["header"] = ""
|
|
10
|
+
self.types = {}
|
|
11
|
+
self.types["header"] = "header"
|
|
12
|
+
self._aliases = {}
|
|
13
|
+
self._processed_titles = []
|
|
14
|
+
self.set_format(format_type)
|
|
15
|
+
|
|
16
|
+
def set_format(self, format_type):
|
|
17
|
+
if format_type == "markdown":
|
|
18
|
+
# markdown levels go deeper, so include subparagraph mapping
|
|
19
|
+
self.level_numbers = {
|
|
20
|
+
"section": 1,
|
|
21
|
+
"subsection": 2,
|
|
22
|
+
"subsubsection": 3,
|
|
23
|
+
"paragraph": 4,
|
|
24
|
+
"subparagraph": 5,
|
|
25
|
+
}
|
|
26
|
+
else:
|
|
27
|
+
# default to latex behavior
|
|
28
|
+
self.level_numbers = {
|
|
29
|
+
"section": 1,
|
|
30
|
+
"subsection": 2,
|
|
31
|
+
"subsubsection": 3,
|
|
32
|
+
"paragraph": 4,
|
|
33
|
+
"subparagraph": 5,
|
|
34
|
+
}
|
|
35
|
+
# map indentation back to section type for outline parsing
|
|
36
|
+
self.inv_prefix = {
|
|
37
|
+
(level - 1) * "\t": section
|
|
38
|
+
for section, level in self.level_numbers.items()
|
|
39
|
+
}
|
|
40
|
+
self.format_type = format_type
|
|
41
|
+
|
|
42
|
+
def start_sec(self, thistype, thistitle):
|
|
43
|
+
assert thistitle not in self.contents.keys(), (
|
|
44
|
+
"more than one section with the name:\n" + thistitle
|
|
45
|
+
)
|
|
46
|
+
self.contents[thistitle] = ""
|
|
47
|
+
self.types[thistitle] = thistype
|
|
48
|
+
print("added", thistitle)
|
|
49
|
+
|
|
50
|
+
def __setstate__(self, d):
|
|
51
|
+
"set the info from a pickle"
|
|
52
|
+
self.contents = d["contents"]
|
|
53
|
+
self.types = d["types"]
|
|
54
|
+
self._aliases = {} # doesn't exist, but still needed
|
|
55
|
+
self._processed_titles = []
|
|
56
|
+
if "format_type" in d:
|
|
57
|
+
self.set_format(d["format_type"])
|
|
58
|
+
else:
|
|
59
|
+
self.set_format("latex")
|
|
60
|
+
return
|
|
61
|
+
|
|
62
|
+
def __getstate__(self):
|
|
63
|
+
"return info for a pickle"
|
|
64
|
+
return {
|
|
65
|
+
"contents": self.contents,
|
|
66
|
+
"types": self.types,
|
|
67
|
+
"format_type": self.format_type,
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
def __iadd__(self, value):
|
|
71
|
+
self.contents[next(reversed(self.contents))] += value
|
|
72
|
+
return self
|
|
73
|
+
|
|
74
|
+
def __str__(self):
|
|
75
|
+
if len(self._processed_titles) > 0:
|
|
76
|
+
raise ValueError(
|
|
77
|
+
"the following section titles are missing from the"
|
|
78
|
+
" outline:\n\t"
|
|
79
|
+
+ "\n\t".join(self._processed_titles)
|
|
80
|
+
+ "\nEvery existing section must appear in the outline, so"
|
|
81
|
+
" that entire sections of text are never dropped by"
|
|
82
|
+
" accident. If you really want to get rid of a section,"
|
|
83
|
+
" add a new heading (e.g. 'for deletion') to the outline"
|
|
84
|
+
" and move the unwanted sections underneath it; then delete"
|
|
85
|
+
" that part of the file by hand."
|
|
86
|
+
)
|
|
87
|
+
retval = ""
|
|
88
|
+
for j in self.contents.keys():
|
|
89
|
+
if self.types[j] != "header":
|
|
90
|
+
new_name = j
|
|
91
|
+
if j in self._aliases.keys():
|
|
92
|
+
new_name = self._aliases[j]
|
|
93
|
+
if self.format_type == "markdown":
|
|
94
|
+
retval += "#" * self.level_numbers[self.types[j]]
|
|
95
|
+
retval += f" {new_name}\n"
|
|
96
|
+
else:
|
|
97
|
+
retval += f"\\{self.types[j]}{{{new_name}}}"
|
|
98
|
+
retval += f"{self.contents[j]}"
|
|
99
|
+
return retval
|
|
100
|
+
|
|
101
|
+
@property
|
|
102
|
+
def outline(self):
|
|
103
|
+
retval = []
|
|
104
|
+
for j in self.contents.keys():
|
|
105
|
+
if self.types[j] != "header":
|
|
106
|
+
indent = (self.level_numbers[self.types[j]] - 1) * "\t"
|
|
107
|
+
thistitle = (indent + "\t").join(j.split("\n"))
|
|
108
|
+
retval.append(indent + "*\t" + thistitle)
|
|
109
|
+
return "\n".join(retval)
|
|
110
|
+
|
|
111
|
+
def outline_in_order(self, outline_lines):
|
|
112
|
+
self._processed_titles = [
|
|
113
|
+
j for j in self.contents.keys() if self.types[j] != "header"
|
|
114
|
+
]
|
|
115
|
+
# {{{ parse (level, title) from each outline line
|
|
116
|
+
parsed = []
|
|
117
|
+
for thisline in outline_lines:
|
|
118
|
+
if not thisline.strip():
|
|
119
|
+
continue
|
|
120
|
+
ilevel = 0
|
|
121
|
+
spacelevel = 0
|
|
122
|
+
hitmarker = False
|
|
123
|
+
for j, thischar in enumerate(thisline):
|
|
124
|
+
if not hitmarker:
|
|
125
|
+
if thischar == " ":
|
|
126
|
+
spacelevel += 1
|
|
127
|
+
if spacelevel == 4 or thischar == "\t":
|
|
128
|
+
ilevel += 1
|
|
129
|
+
spacelevel = 0
|
|
130
|
+
elif thischar == "*":
|
|
131
|
+
hitmarker = True
|
|
132
|
+
else:
|
|
133
|
+
assert thischar in [" ", "\t"]
|
|
134
|
+
title = thisline[j + 1 :]
|
|
135
|
+
break
|
|
136
|
+
if not hitmarker:
|
|
137
|
+
raise ValueError("somehow, there wasn't a * marker!")
|
|
138
|
+
parsed.append((ilevel, title))
|
|
139
|
+
# }}}
|
|
140
|
+
# {{{ resolve titles that aren't existing sections -- only sections
|
|
141
|
+
# that appear nowhere in the outline can have been renamed
|
|
142
|
+
outline_titles = {title for _, title in parsed}
|
|
143
|
+
orphans = [
|
|
144
|
+
j
|
|
145
|
+
for j in self.contents.keys()
|
|
146
|
+
if self.types[j] != "header" and j not in outline_titles
|
|
147
|
+
]
|
|
148
|
+
renamed = {}
|
|
149
|
+
for _, title in parsed:
|
|
150
|
+
if title in self.contents.keys() or title in renamed:
|
|
151
|
+
continue
|
|
152
|
+
if re.search(r"\bNEW\b", title):
|
|
153
|
+
print(f"adding\n\t{title}\nas a new section (labeled NEW)")
|
|
154
|
+
elif len(orphans) == 0:
|
|
155
|
+
print(
|
|
156
|
+
f"adding\n\t{title}\nas a new section (no unplaced"
|
|
157
|
+
" sections are left that it could be a rename of)"
|
|
158
|
+
)
|
|
159
|
+
else:
|
|
160
|
+
ranked = sorted(
|
|
161
|
+
orphans,
|
|
162
|
+
key=lambda j: fuzz.token_sort_ratio(title, j),
|
|
163
|
+
reverse=True,
|
|
164
|
+
)[:5]
|
|
165
|
+
choices = "".join(
|
|
166
|
+
f"\n {n + 1}) [{fuzz.token_sort_ratio(title, j)}] {j}"
|
|
167
|
+
for n, j in enumerate(ranked)
|
|
168
|
+
)
|
|
169
|
+
prompt = (
|
|
170
|
+
f"didn't find\n\t{title}\namong the existing sections."
|
|
171
|
+
" Is it a renamed version of one of these sections that"
|
|
172
|
+
f" don't appear in the outline?{choices}\nenter a number"
|
|
173
|
+
" to rename, n to add it as a new, empty section, or q"
|
|
174
|
+
" to quit without writing anything: "
|
|
175
|
+
)
|
|
176
|
+
while True:
|
|
177
|
+
answer = input(prompt).strip()
|
|
178
|
+
if answer in ("n", "q"):
|
|
179
|
+
break
|
|
180
|
+
if answer.isdigit() and 1 <= int(answer) <= len(ranked):
|
|
181
|
+
break
|
|
182
|
+
print(f"didn't understand {answer!r}")
|
|
183
|
+
if answer == "q":
|
|
184
|
+
raise ValueError("aborted -- nothing was written")
|
|
185
|
+
if answer != "n":
|
|
186
|
+
original = ranked[int(answer) - 1]
|
|
187
|
+
self._aliases[original] = title # will be replaced later
|
|
188
|
+
renamed[title] = original
|
|
189
|
+
orphans.remove(original)
|
|
190
|
+
continue
|
|
191
|
+
# a new section, so it isn't on the list of titles to place
|
|
192
|
+
self.contents[title] = ""
|
|
193
|
+
self._processed_titles.append(title)
|
|
194
|
+
# }}}
|
|
195
|
+
# {{{ reorder sections to match the outline
|
|
196
|
+
for ilevel, title in parsed:
|
|
197
|
+
title = renamed.get(title, title)
|
|
198
|
+
if title not in self._processed_titles:
|
|
199
|
+
raise ValueError(
|
|
200
|
+
f"the section\n\t{title}\nappears more than once in the"
|
|
201
|
+
" outline"
|
|
202
|
+
)
|
|
203
|
+
self.contents.move_to_end(title)
|
|
204
|
+
self._processed_titles.remove(title)
|
|
205
|
+
self.types[title] = self.inv_prefix[ilevel * "\t"]
|
|
206
|
+
# }}}
|
|
@@ -230,8 +230,9 @@ class PlanComparison:
|
|
|
230
230
|
before.get("text", ""), after.get("text", ""), width
|
|
231
231
|
)
|
|
232
232
|
if state in ("added", "removed"):
|
|
233
|
-
|
|
234
|
-
|
|
233
|
+
node["_comparison_status"] = (
|
|
234
|
+
"added" if state == "added" else "deleted"
|
|
235
|
+
)
|
|
235
236
|
if previous is not None and previous != name:
|
|
236
237
|
body = (
|
|
237
238
|
f'<font color="#c62828"><s>{html.escape(previous)}</s>'
|
|
@@ -327,5 +328,3 @@ class PlanComparison:
|
|
|
327
328
|
item.set("fill", "#888888")
|
|
328
329
|
if state == "removed":
|
|
329
330
|
item.set("text-decoration", "line-through")
|
|
330
|
-
elif state == "changed" and item.text in self.node_states:
|
|
331
|
-
item.set("fill", "#16803c")
|
|
@@ -477,8 +477,11 @@ def _append_node(
|
|
|
477
477
|
)
|
|
478
478
|
if "_comparison_label" in node:
|
|
479
479
|
label = "<" + node["_comparison_label"] + ">"
|
|
480
|
+
status = node.get("_comparison_status")
|
|
481
|
+
status_prefix = f"<b>{status}:</b> " if status else ""
|
|
480
482
|
task_link_line = (
|
|
481
|
-
f'<font point-size="9">
|
|
483
|
+
f'<font point-size="9">{status_prefix}'
|
|
484
|
+
f"__WGRPH_TASK_LINK__:{node_name}</font>"
|
|
482
485
|
)
|
|
483
486
|
if label:
|
|
484
487
|
label = "<" + task_link_line + '<br align="left"/>' + label[1:]
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Vendored unmodified from svg-pan-zoom 3.6.2 (BSD 2-Clause license):
|
|
3
|
+
*
|
|
4
|
+
* Copyright 2009-2010 Andrea Leofreddi <a.leofreddi@vleo.net>
|
|
5
|
+
* All rights reserved.
|
|
6
|
+
*
|
|
7
|
+
* Redistribution and use in source and binary forms, with or without modification,
|
|
8
|
+
* are permitted provided that the following conditions are met:
|
|
9
|
+
*
|
|
10
|
+
* * Redistributions of source code must retain the above copyright notice, this
|
|
11
|
+
* list of conditions and the following disclaimer.
|
|
12
|
+
*
|
|
13
|
+
* * Redistributions in binary form must reproduce the above copyright notice, this
|
|
14
|
+
* list of conditions and the following disclaimer in the documentation and/or
|
|
15
|
+
* other materials provided with the distribution.
|
|
16
|
+
*
|
|
17
|
+
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
|
18
|
+
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
19
|
+
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
20
|
+
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
|
|
21
|
+
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
22
|
+
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
23
|
+
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
|
24
|
+
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
25
|
+
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
26
|
+
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
27
|
+
*/
|
|
28
|
+
// svg-pan-zoom v3.6.2
|
|
29
|
+
// https://github.com/bumbu/svg-pan-zoom
|
|
30
|
+
!function s(r,a,l){function u(e,t){if(!a[e]){if(!r[e]){var o="function"==typeof require&&require;if(!t&&o)return o(e,!0);if(h)return h(e,!0);var n=new Error("Cannot find module '"+e+"'");throw n.code="MODULE_NOT_FOUND",n}var i=a[e]={exports:{}};r[e][0].call(i.exports,function(t){return u(r[e][1][t]||t)},i,i.exports,s,r,a,l)}return a[e].exports}for(var h="function"==typeof require&&require,t=0;t<l.length;t++)u(l[t]);return u}({1:[function(t,e,o){var s=t("./svg-utilities");e.exports={enable:function(t){var e=t.svg.querySelector("defs");if(e||(e=document.createElementNS(s.svgNS,"defs"),t.svg.appendChild(e)),!e.querySelector("style#svg-pan-zoom-controls-styles")){var o=document.createElementNS(s.svgNS,"style");o.setAttribute("id","svg-pan-zoom-controls-styles"),o.setAttribute("type","text/css"),o.textContent=".svg-pan-zoom-control { cursor: pointer; fill: black; fill-opacity: 0.333; } .svg-pan-zoom-control:hover { fill-opacity: 0.8; } .svg-pan-zoom-control-background { fill: white; fill-opacity: 0.5; } .svg-pan-zoom-control-background { fill-opacity: 0.8; }",e.appendChild(o)}var n=document.createElementNS(s.svgNS,"g");n.setAttribute("id","svg-pan-zoom-controls"),n.setAttribute("transform","translate("+(t.width-70)+" "+(t.height-76)+") scale(0.75)"),n.setAttribute("class","svg-pan-zoom-control"),n.appendChild(this._createZoomIn(t)),n.appendChild(this._createZoomReset(t)),n.appendChild(this._createZoomOut(t)),t.svg.appendChild(n),t.controlIcons=n},_createZoomIn:function(t){var e=document.createElementNS(s.svgNS,"g");e.setAttribute("id","svg-pan-zoom-zoom-in"),e.setAttribute("transform","translate(30.5 5) scale(0.015)"),e.setAttribute("class","svg-pan-zoom-control"),e.addEventListener("click",function(){t.getPublicInstance().zoomIn()},!1),e.addEventListener("touchstart",function(){t.getPublicInstance().zoomIn()},!1);var o=document.createElementNS(s.svgNS,"rect");o.setAttribute("x","0"),o.setAttribute("y","0"),o.setAttribute("width","1500"),o.setAttribute("height","1400"),o.setAttribute("class","svg-pan-zoom-control-background"),e.appendChild(o);var n=document.createElementNS(s.svgNS,"path");return n.setAttribute("d","M1280 576v128q0 26 -19 45t-45 19h-320v320q0 26 -19 45t-45 19h-128q-26 0 -45 -19t-19 -45v-320h-320q-26 0 -45 -19t-19 -45v-128q0 -26 19 -45t45 -19h320v-320q0 -26 19 -45t45 -19h128q26 0 45 19t19 45v320h320q26 0 45 19t19 45zM1536 1120v-960 q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z"),n.setAttribute("class","svg-pan-zoom-control-element"),e.appendChild(n),e},_createZoomReset:function(t){var e=document.createElementNS(s.svgNS,"g");e.setAttribute("id","svg-pan-zoom-reset-pan-zoom"),e.setAttribute("transform","translate(5 35) scale(0.4)"),e.setAttribute("class","svg-pan-zoom-control"),e.addEventListener("click",function(){t.getPublicInstance().reset()},!1),e.addEventListener("touchstart",function(){t.getPublicInstance().reset()},!1);var o=document.createElementNS(s.svgNS,"rect");o.setAttribute("x","2"),o.setAttribute("y","2"),o.setAttribute("width","182"),o.setAttribute("height","58"),o.setAttribute("class","svg-pan-zoom-control-background"),e.appendChild(o);var n=document.createElementNS(s.svgNS,"path");n.setAttribute("d","M33.051,20.632c-0.742-0.406-1.854-0.609-3.338-0.609h-7.969v9.281h7.769c1.543,0,2.701-0.188,3.473-0.562c1.365-0.656,2.048-1.953,2.048-3.891C35.032,22.757,34.372,21.351,33.051,20.632z"),n.setAttribute("class","svg-pan-zoom-control-element"),e.appendChild(n);var i=document.createElementNS(s.svgNS,"path");return i.setAttribute("d","M170.231,0.5H15.847C7.102,0.5,0.5,5.708,0.5,11.84v38.861C0.5,56.833,7.102,61.5,15.847,61.5h154.384c8.745,0,15.269-4.667,15.269-10.798V11.84C185.5,5.708,178.976,0.5,170.231,0.5z M42.837,48.569h-7.969c-0.219-0.766-0.375-1.383-0.469-1.852c-0.188-0.969-0.289-1.961-0.305-2.977l-0.047-3.211c-0.03-2.203-0.41-3.672-1.142-4.406c-0.732-0.734-2.103-1.102-4.113-1.102h-7.05v13.547h-7.055V14.022h16.524c2.361,0.047,4.178,0.344,5.45,0.891c1.272,0.547,2.351,1.352,3.234,2.414c0.731,0.875,1.31,1.844,1.737,2.906s0.64,2.273,0.64,3.633c0,1.641-0.414,3.254-1.242,4.84s-2.195,2.707-4.102,3.363c1.594,0.641,2.723,1.551,3.387,2.73s0.996,2.98,0.996,5.402v2.32c0,1.578,0.063,2.648,0.19,3.211c0.19,0.891,0.635,1.547,1.333,1.969V48.569z M75.579,48.569h-26.18V14.022h25.336v6.117H56.454v7.336h16.781v6H56.454v8.883h19.125V48.569z M104.497,46.331c-2.44,2.086-5.887,3.129-10.34,3.129c-4.548,0-8.125-1.027-10.731-3.082s-3.909-4.879-3.909-8.473h6.891c0.224,1.578,0.662,2.758,1.316,3.539c1.196,1.422,3.246,2.133,6.15,2.133c1.739,0,3.151-0.188,4.236-0.562c2.058-0.719,3.087-2.055,3.087-4.008c0-1.141-0.504-2.023-1.512-2.648c-1.008-0.609-2.607-1.148-4.796-1.617l-3.74-0.82c-3.676-0.812-6.201-1.695-7.576-2.648c-2.328-1.594-3.492-4.086-3.492-7.477c0-3.094,1.139-5.664,3.417-7.711s5.623-3.07,10.036-3.07c3.685,0,6.829,0.965,9.431,2.895c2.602,1.93,3.966,4.73,4.093,8.402h-6.938c-0.128-2.078-1.057-3.555-2.787-4.43c-1.154-0.578-2.587-0.867-4.301-0.867c-1.907,0-3.428,0.375-4.565,1.125c-1.138,0.75-1.706,1.797-1.706,3.141c0,1.234,0.561,2.156,1.682,2.766c0.721,0.406,2.25,0.883,4.589,1.43l6.063,1.43c2.657,0.625,4.648,1.461,5.975,2.508c2.059,1.625,3.089,3.977,3.089,7.055C108.157,41.624,106.937,44.245,104.497,46.331z M139.61,48.569h-26.18V14.022h25.336v6.117h-18.281v7.336h16.781v6h-16.781v8.883h19.125V48.569z M170.337,20.14h-10.336v28.43h-7.266V20.14h-10.383v-6.117h27.984V20.14z"),i.setAttribute("class","svg-pan-zoom-control-element"),e.appendChild(i),e},_createZoomOut:function(t){var e=document.createElementNS(s.svgNS,"g");e.setAttribute("id","svg-pan-zoom-zoom-out"),e.setAttribute("transform","translate(30.5 70) scale(0.015)"),e.setAttribute("class","svg-pan-zoom-control"),e.addEventListener("click",function(){t.getPublicInstance().zoomOut()},!1),e.addEventListener("touchstart",function(){t.getPublicInstance().zoomOut()},!1);var o=document.createElementNS(s.svgNS,"rect");o.setAttribute("x","0"),o.setAttribute("y","0"),o.setAttribute("width","1500"),o.setAttribute("height","1400"),o.setAttribute("class","svg-pan-zoom-control-background"),e.appendChild(o);var n=document.createElementNS(s.svgNS,"path");return n.setAttribute("d","M1280 576v128q0 26 -19 45t-45 19h-896q-26 0 -45 -19t-19 -45v-128q0 -26 19 -45t45 -19h896q26 0 45 19t19 45zM1536 1120v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5 t84.5 -203.5z"),n.setAttribute("class","svg-pan-zoom-control-element"),e.appendChild(n),e},disable:function(t){t.controlIcons&&(t.controlIcons.parentNode.removeChild(t.controlIcons),t.controlIcons=null)}}},{"./svg-utilities":5}],2:[function(t,e,o){function n(t,e){this.init(t,e)}var i=t("./svg-utilities"),r=t("./utilities");n.prototype.init=function(t,e){this.viewport=t,this.options=e,this.originalState={zoom:1,x:0,y:0},this.activeState={zoom:1,x:0,y:0},this.updateCTMCached=r.proxy(this.updateCTM,this),this.requestAnimationFrame=r.createRequestAnimationFrame(this.options.refreshRate),this.viewBox={x:0,y:0,width:0,height:0},this.cacheViewBox();var o=this.processCTM();this.setCTM(o),this.updateCTM()},n.prototype.cacheViewBox=function(){var t=this.options.svg.getAttribute("viewBox");if(t){var e=t.split(/[\s\,]/).filter(function(t){return t}).map(parseFloat);this.viewBox.x=e[0],this.viewBox.y=e[1],this.viewBox.width=e[2],this.viewBox.height=e[3];var o=Math.min(this.options.width/this.viewBox.width,this.options.height/this.viewBox.height);this.activeState.zoom=o,this.activeState.x=(this.options.width-this.viewBox.width*o)/2,this.activeState.y=(this.options.height-this.viewBox.height*o)/2,this.updateCTMOnNextFrame(),this.options.svg.removeAttribute("viewBox")}else this.simpleViewBoxCache()},n.prototype.simpleViewBoxCache=function(){var t=this.viewport.getBBox();this.viewBox.x=t.x,this.viewBox.y=t.y,this.viewBox.width=t.width,this.viewBox.height=t.height},n.prototype.getViewBox=function(){return r.extend({},this.viewBox)},n.prototype.processCTM=function(){var t,e=this.getCTM();(this.options.fit||this.options.contain)&&(t=this.options.fit?Math.min(this.options.width/this.viewBox.width,this.options.height/this.viewBox.height):Math.max(this.options.width/this.viewBox.width,this.options.height/this.viewBox.height),e.a=t,e.d=t,e.e=-this.viewBox.x*t,e.f=-this.viewBox.y*t);if(this.options.center){var o=.5*(this.options.width-(this.viewBox.width+2*this.viewBox.x)*e.a),n=.5*(this.options.height-(this.viewBox.height+2*this.viewBox.y)*e.a);e.e=o,e.f=n}return this.originalState.zoom=e.a,this.originalState.x=e.e,this.originalState.y=e.f,e},n.prototype.getOriginalState=function(){return r.extend({},this.originalState)},n.prototype.getState=function(){return r.extend({},this.activeState)},n.prototype.getZoom=function(){return this.activeState.zoom},n.prototype.getRelativeZoom=function(){return this.activeState.zoom/this.originalState.zoom},n.prototype.computeRelativeZoom=function(t){return t/this.originalState.zoom},n.prototype.getPan=function(){return{x:this.activeState.x,y:this.activeState.y}},n.prototype.getCTM=function(){var t=this.options.svg.createSVGMatrix();return t.a=this.activeState.zoom,t.b=0,t.c=0,t.d=this.activeState.zoom,t.e=this.activeState.x,t.f=this.activeState.y,t},n.prototype.setCTM=function(t){var e=this.isZoomDifferent(t),o=this.isPanDifferent(t);if(e||o){if(e&&(!1===this.options.beforeZoom(this.getRelativeZoom(),this.computeRelativeZoom(t.a))?(t.a=t.d=this.activeState.zoom,e=!1):(this.updateCache(t),this.options.onZoom(this.getRelativeZoom()))),o){var n=this.options.beforePan(this.getPan(),{x:t.e,y:t.f}),i=!1,s=!1;!1===n?(t.e=this.getPan().x,t.f=this.getPan().y,i=s=!0):r.isObject(n)&&(!1===n.x?(t.e=this.getPan().x,i=!0):r.isNumber(n.x)&&(t.e=n.x),!1===n.y?(t.f=this.getPan().y,s=!0):r.isNumber(n.y)&&(t.f=n.y)),i&&s||!this.isPanDifferent(t)?o=!1:(this.updateCache(t),this.options.onPan(this.getPan()))}(e||o)&&this.updateCTMOnNextFrame()}},n.prototype.isZoomDifferent=function(t){return this.activeState.zoom!==t.a},n.prototype.isPanDifferent=function(t){return this.activeState.x!==t.e||this.activeState.y!==t.f},n.prototype.updateCache=function(t){this.activeState.zoom=t.a,this.activeState.x=t.e,this.activeState.y=t.f},n.prototype.pendingUpdate=!1,n.prototype.updateCTMOnNextFrame=function(){this.pendingUpdate||(this.pendingUpdate=!0,this.requestAnimationFrame.call(window,this.updateCTMCached))},n.prototype.updateCTM=function(){var t=this.getCTM();i.setCTM(this.viewport,t,this.defs),this.pendingUpdate=!1,this.options.onUpdatedCTM&&this.options.onUpdatedCTM(t)},e.exports=function(t,e){return new n(t,e)}},{"./svg-utilities":5,"./utilities":7}],3:[function(t,e,o){var n,i=t("./svg-pan-zoom.js");n=window,document,"function"==typeof define&&define.amd?define("svg-pan-zoom",function(){return i}):void 0!==e&&e.exports&&(e.exports=i,n.svgPanZoom=i)},{"./svg-pan-zoom.js":4}],4:[function(t,e,o){function i(t,e){this.init(t,e)}var n=t("./uniwheel"),s=t("./control-icons"),r=t("./utilities"),a=t("./svg-utilities"),l=t("./shadow-viewport"),u={viewportSelector:".svg-pan-zoom_viewport",panEnabled:!0,controlIconsEnabled:!1,zoomEnabled:!0,dblClickZoomEnabled:!0,mouseWheelZoomEnabled:!0,preventMouseEventsDefault:!0,zoomScaleSensitivity:.1,minZoom:.5,maxZoom:10,fit:!0,contain:!1,center:!0,refreshRate:"auto",beforeZoom:null,onZoom:null,beforePan:null,onPan:null,customEventsHandler:null,eventsListenerElement:null,onUpdatedCTM:null},h={passive:!0};i.prototype.init=function(t,e){var o=this;this.svg=t,this.defs=t.querySelector("defs"),a.setupSvgAttributes(this.svg),this.options=r.extend(r.extend({},u),e),this.state="none";var n=a.getBoundingClientRectNormalized(t);this.width=n.width,this.height=n.height,this.viewport=l(a.getOrCreateViewport(this.svg,this.options.viewportSelector),{svg:this.svg,width:this.width,height:this.height,fit:this.options.fit,contain:this.options.contain,center:this.options.center,refreshRate:this.options.refreshRate,beforeZoom:function(t,e){if(o.viewport&&o.options.beforeZoom)return o.options.beforeZoom(t,e)},onZoom:function(t){if(o.viewport&&o.options.onZoom)return o.options.onZoom(t)},beforePan:function(t,e){if(o.viewport&&o.options.beforePan)return o.options.beforePan(t,e)},onPan:function(t){if(o.viewport&&o.options.onPan)return o.options.onPan(t)},onUpdatedCTM:function(t){if(o.viewport&&o.options.onUpdatedCTM)return o.options.onUpdatedCTM(t)}});var i=this.getPublicInstance();i.setBeforeZoom(this.options.beforeZoom),i.setOnZoom(this.options.onZoom),i.setBeforePan(this.options.beforePan),i.setOnPan(this.options.onPan),i.setOnUpdatedCTM(this.options.onUpdatedCTM),this.options.controlIconsEnabled&&s.enable(this),this.lastMouseWheelEventTime=Date.now(),this.setupHandlers()},i.prototype.setupHandlers=function(){var o=this,n=null;if(this.eventListeners={mousedown:function(t){var e=o.handleMouseDown(t,n);return n=t,e},touchstart:function(t){var e=o.handleMouseDown(t,n);return n=t,e},mouseup:function(t){return o.handleMouseUp(t)},touchend:function(t){return o.handleMouseUp(t)},mousemove:function(t){return o.handleMouseMove(t)},touchmove:function(t){return o.handleMouseMove(t)},mouseleave:function(t){return o.handleMouseUp(t)},touchleave:function(t){return o.handleMouseUp(t)},touchcancel:function(t){return o.handleMouseUp(t)}},null!=this.options.customEventsHandler){this.options.customEventsHandler.init({svgElement:this.svg,eventsListenerElement:this.options.eventsListenerElement,instance:this.getPublicInstance()});var t=this.options.customEventsHandler.haltEventListeners;if(t&&t.length)for(var e=t.length-1;0<=e;e--)this.eventListeners.hasOwnProperty(t[e])&&delete this.eventListeners[t[e]]}for(var i in this.eventListeners)(this.options.eventsListenerElement||this.svg).addEventListener(i,this.eventListeners[i],!this.options.preventMouseEventsDefault&&h);this.options.mouseWheelZoomEnabled&&(this.options.mouseWheelZoomEnabled=!1,this.enableMouseWheelZoom())},i.prototype.enableMouseWheelZoom=function(){if(!this.options.mouseWheelZoomEnabled){var e=this;this.wheelListener=function(t){return e.handleMouseWheel(t)};var t=!this.options.preventMouseEventsDefault;n.on(this.options.eventsListenerElement||this.svg,this.wheelListener,t),this.options.mouseWheelZoomEnabled=!0}},i.prototype.disableMouseWheelZoom=function(){if(this.options.mouseWheelZoomEnabled){var t=!this.options.preventMouseEventsDefault;n.off(this.options.eventsListenerElement||this.svg,this.wheelListener,t),this.options.mouseWheelZoomEnabled=!1}},i.prototype.handleMouseWheel=function(t){if(this.options.zoomEnabled&&"none"===this.state){this.options.preventMouseEventsDefault&&(t.preventDefault?t.preventDefault():t.returnValue=!1);var e=t.deltaY||1,o=Date.now()-this.lastMouseWheelEventTime,n=3+Math.max(0,30-o);this.lastMouseWheelEventTime=Date.now(),"deltaMode"in t&&0===t.deltaMode&&t.wheelDelta&&(e=0===t.deltaY?0:Math.abs(t.wheelDelta)/t.deltaY),e=-.3<e&&e<.3?e:(0<e?1:-1)*Math.log(Math.abs(e)+10)/n;var i=this.svg.getScreenCTM().inverse(),s=a.getEventPoint(t,this.svg).matrixTransform(i),r=Math.pow(1+this.options.zoomScaleSensitivity,-1*e);this.zoomAtPoint(r,s)}},i.prototype.zoomAtPoint=function(t,e,o){var n=this.viewport.getOriginalState();o?(t=Math.max(this.options.minZoom*n.zoom,Math.min(this.options.maxZoom*n.zoom,t)),t/=this.getZoom()):this.getZoom()*t<this.options.minZoom*n.zoom?t=this.options.minZoom*n.zoom/this.getZoom():this.getZoom()*t>this.options.maxZoom*n.zoom&&(t=this.options.maxZoom*n.zoom/this.getZoom());var i=this.viewport.getCTM(),s=e.matrixTransform(i.inverse()),r=this.svg.createSVGMatrix().translate(s.x,s.y).scale(t).translate(-s.x,-s.y),a=i.multiply(r);a.a!==i.a&&this.viewport.setCTM(a)},i.prototype.zoom=function(t,e){this.zoomAtPoint(t,a.getSvgCenterPoint(this.svg,this.width,this.height),e)},i.prototype.publicZoom=function(t,e){e&&(t=this.computeFromRelativeZoom(t)),this.zoom(t,e)},i.prototype.publicZoomAtPoint=function(t,e,o){if(o&&(t=this.computeFromRelativeZoom(t)),"SVGPoint"!==r.getType(e)){if(!("x"in e&&"y"in e))throw new Error("Given point is invalid");e=a.createSVGPoint(this.svg,e.x,e.y)}this.zoomAtPoint(t,e,o)},i.prototype.getZoom=function(){return this.viewport.getZoom()},i.prototype.getRelativeZoom=function(){return this.viewport.getRelativeZoom()},i.prototype.computeFromRelativeZoom=function(t){return t*this.viewport.getOriginalState().zoom},i.prototype.resetZoom=function(){var t=this.viewport.getOriginalState();this.zoom(t.zoom,!0)},i.prototype.resetPan=function(){this.pan(this.viewport.getOriginalState())},i.prototype.reset=function(){this.resetZoom(),this.resetPan()},i.prototype.handleDblClick=function(t){var e;if((this.options.preventMouseEventsDefault&&(t.preventDefault?t.preventDefault():t.returnValue=!1),this.options.controlIconsEnabled)&&-1<(t.target.getAttribute("class")||"").indexOf("svg-pan-zoom-control"))return!1;e=t.shiftKey?1/(2*(1+this.options.zoomScaleSensitivity)):2*(1+this.options.zoomScaleSensitivity);var o=a.getEventPoint(t,this.svg).matrixTransform(this.svg.getScreenCTM().inverse());this.zoomAtPoint(e,o)},i.prototype.handleMouseDown=function(t,e){this.options.preventMouseEventsDefault&&(t.preventDefault?t.preventDefault():t.returnValue=!1),r.mouseAndTouchNormalize(t,this.svg),this.options.dblClickZoomEnabled&&r.isDblClick(t,e)?this.handleDblClick(t):(this.state="pan",this.firstEventCTM=this.viewport.getCTM(),this.stateOrigin=a.getEventPoint(t,this.svg).matrixTransform(this.firstEventCTM.inverse()))},i.prototype.handleMouseMove=function(t){if(this.options.preventMouseEventsDefault&&(t.preventDefault?t.preventDefault():t.returnValue=!1),"pan"===this.state&&this.options.panEnabled){var e=a.getEventPoint(t,this.svg).matrixTransform(this.firstEventCTM.inverse()),o=this.firstEventCTM.translate(e.x-this.stateOrigin.x,e.y-this.stateOrigin.y);this.viewport.setCTM(o)}},i.prototype.handleMouseUp=function(t){this.options.preventMouseEventsDefault&&(t.preventDefault?t.preventDefault():t.returnValue=!1),"pan"===this.state&&(this.state="none")},i.prototype.fit=function(){var t=this.viewport.getViewBox(),e=Math.min(this.width/t.width,this.height/t.height);this.zoom(e,!0)},i.prototype.contain=function(){var t=this.viewport.getViewBox(),e=Math.max(this.width/t.width,this.height/t.height);this.zoom(e,!0)},i.prototype.center=function(){var t=this.viewport.getViewBox(),e=.5*(this.width-(t.width+2*t.x)*this.getZoom()),o=.5*(this.height-(t.height+2*t.y)*this.getZoom());this.getPublicInstance().pan({x:e,y:o})},i.prototype.updateBBox=function(){this.viewport.simpleViewBoxCache()},i.prototype.pan=function(t){var e=this.viewport.getCTM();e.e=t.x,e.f=t.y,this.viewport.setCTM(e)},i.prototype.panBy=function(t){var e=this.viewport.getCTM();e.e+=t.x,e.f+=t.y,this.viewport.setCTM(e)},i.prototype.getPan=function(){var t=this.viewport.getState();return{x:t.x,y:t.y}},i.prototype.resize=function(){var t=a.getBoundingClientRectNormalized(this.svg);this.width=t.width,this.height=t.height;var e=this.viewport;e.options.width=this.width,e.options.height=this.height,e.processCTM(),this.options.controlIconsEnabled&&(this.getPublicInstance().disableControlIcons(),this.getPublicInstance().enableControlIcons())},i.prototype.destroy=function(){var e=this;for(var t in this.beforeZoom=null,this.onZoom=null,this.beforePan=null,this.onPan=null,(this.onUpdatedCTM=null)!=this.options.customEventsHandler&&this.options.customEventsHandler.destroy({svgElement:this.svg,eventsListenerElement:this.options.eventsListenerElement,instance:this.getPublicInstance()}),this.eventListeners)(this.options.eventsListenerElement||this.svg).removeEventListener(t,this.eventListeners[t],!this.options.preventMouseEventsDefault&&h);this.disableMouseWheelZoom(),this.getPublicInstance().disableControlIcons(),this.reset(),c=c.filter(function(t){return t.svg!==e.svg}),delete this.options,delete this.viewport,delete this.publicInstance,delete this.pi,this.getPublicInstance=function(){return null}},i.prototype.getPublicInstance=function(){var o=this;return this.publicInstance||(this.publicInstance=this.pi={enablePan:function(){return o.options.panEnabled=!0,o.pi},disablePan:function(){return o.options.panEnabled=!1,o.pi},isPanEnabled:function(){return!!o.options.panEnabled},pan:function(t){return o.pan(t),o.pi},panBy:function(t){return o.panBy(t),o.pi},getPan:function(){return o.getPan()},setBeforePan:function(t){return o.options.beforePan=null===t?null:r.proxy(t,o.publicInstance),o.pi},setOnPan:function(t){return o.options.onPan=null===t?null:r.proxy(t,o.publicInstance),o.pi},enableZoom:function(){return o.options.zoomEnabled=!0,o.pi},disableZoom:function(){return o.options.zoomEnabled=!1,o.pi},isZoomEnabled:function(){return!!o.options.zoomEnabled},enableControlIcons:function(){return o.options.controlIconsEnabled||(o.options.controlIconsEnabled=!0,s.enable(o)),o.pi},disableControlIcons:function(){return o.options.controlIconsEnabled&&(o.options.controlIconsEnabled=!1,s.disable(o)),o.pi},isControlIconsEnabled:function(){return!!o.options.controlIconsEnabled},enableDblClickZoom:function(){return o.options.dblClickZoomEnabled=!0,o.pi},disableDblClickZoom:function(){return o.options.dblClickZoomEnabled=!1,o.pi},isDblClickZoomEnabled:function(){return!!o.options.dblClickZoomEnabled},enableMouseWheelZoom:function(){return o.enableMouseWheelZoom(),o.pi},disableMouseWheelZoom:function(){return o.disableMouseWheelZoom(),o.pi},isMouseWheelZoomEnabled:function(){return!!o.options.mouseWheelZoomEnabled},setZoomScaleSensitivity:function(t){return o.options.zoomScaleSensitivity=t,o.pi},setMinZoom:function(t){return o.options.minZoom=t,o.pi},setMaxZoom:function(t){return o.options.maxZoom=t,o.pi},setBeforeZoom:function(t){return o.options.beforeZoom=null===t?null:r.proxy(t,o.publicInstance),o.pi},setOnZoom:function(t){return o.options.onZoom=null===t?null:r.proxy(t,o.publicInstance),o.pi},zoom:function(t){return o.publicZoom(t,!0),o.pi},zoomBy:function(t){return o.publicZoom(t,!1),o.pi},zoomAtPoint:function(t,e){return o.publicZoomAtPoint(t,e,!0),o.pi},zoomAtPointBy:function(t,e){return o.publicZoomAtPoint(t,e,!1),o.pi},zoomIn:function(){return this.zoomBy(1+o.options.zoomScaleSensitivity),o.pi},zoomOut:function(){return this.zoomBy(1/(1+o.options.zoomScaleSensitivity)),o.pi},getZoom:function(){return o.getRelativeZoom()},setOnUpdatedCTM:function(t){return o.options.onUpdatedCTM=null===t?null:r.proxy(t,o.publicInstance),o.pi},resetZoom:function(){return o.resetZoom(),o.pi},resetPan:function(){return o.resetPan(),o.pi},reset:function(){return o.reset(),o.pi},fit:function(){return o.fit(),o.pi},contain:function(){return o.contain(),o.pi},center:function(){return o.center(),o.pi},updateBBox:function(){return o.updateBBox(),o.pi},resize:function(){return o.resize(),o.pi},getSizes:function(){return{width:o.width,height:o.height,realZoom:o.getZoom(),viewBox:o.viewport.getViewBox()}},destroy:function(){return o.destroy(),o.pi}}),this.publicInstance};var c=[];e.exports=function(t,e){var o=r.getSvg(t);if(null===o)return null;for(var n=c.length-1;0<=n;n--)if(c[n].svg===o)return c[n].instance.getPublicInstance();return c.push({svg:o,instance:new i(o,e)}),c[c.length-1].instance.getPublicInstance()}},{"./control-icons":1,"./shadow-viewport":2,"./svg-utilities":5,"./uniwheel":6,"./utilities":7}],5:[function(t,e,o){var l=t("./utilities"),s="unknown";document.documentMode&&(s="ie"),e.exports={svgNS:"http://www.w3.org/2000/svg",xmlNS:"http://www.w3.org/XML/1998/namespace",xmlnsNS:"http://www.w3.org/2000/xmlns/",xlinkNS:"http://www.w3.org/1999/xlink",evNS:"http://www.w3.org/2001/xml-events",getBoundingClientRectNormalized:function(t){if(t.clientWidth&&t.clientHeight)return{width:t.clientWidth,height:t.clientHeight};if(t.getBoundingClientRect())return t.getBoundingClientRect();throw new Error("Cannot get BoundingClientRect for SVG.")},getOrCreateViewport:function(t,e){var o=null;if(!(o=l.isElement(e)?e:t.querySelector(e))){var n=Array.prototype.slice.call(t.childNodes||t.children).filter(function(t){return"defs"!==t.nodeName&&"#text"!==t.nodeName});1===n.length&&"g"===n[0].nodeName&&null===n[0].getAttribute("transform")&&(o=n[0])}if(!o){var i="viewport-"+(new Date).toISOString().replace(/\D/g,"");(o=document.createElementNS(this.svgNS,"g")).setAttribute("id",i);var s=t.childNodes||t.children;if(s&&0<s.length)for(var r=s.length;0<r;r--)"defs"!==s[s.length-r].nodeName&&o.appendChild(s[s.length-r]);t.appendChild(o)}var a=[];return o.getAttribute("class")&&(a=o.getAttribute("class").split(" ")),~a.indexOf("svg-pan-zoom_viewport")||(a.push("svg-pan-zoom_viewport"),o.setAttribute("class",a.join(" "))),o},setupSvgAttributes:function(t){if(t.setAttribute("xmlns",this.svgNS),t.setAttributeNS(this.xmlnsNS,"xmlns:xlink",this.xlinkNS),t.setAttributeNS(this.xmlnsNS,"xmlns:ev",this.evNS),null!==t.parentNode){var e=t.getAttribute("style")||"";-1===e.toLowerCase().indexOf("overflow")&&t.setAttribute("style","overflow: hidden; "+e)}},internetExplorerRedisplayInterval:300,refreshDefsGlobal:l.throttle(function(){for(var t=document.querySelectorAll("defs"),e=t.length,o=0;o<e;o++){var n=t[o];n.parentNode.insertBefore(n,n)}},this?this.internetExplorerRedisplayInterval:null),setCTM:function(t,e,o){var n=this,i="matrix("+e.a+","+e.b+","+e.c+","+e.d+","+e.e+","+e.f+")";t.setAttributeNS(null,"transform",i),"transform"in t.style?t.style.transform=i:"-ms-transform"in t.style?t.style["-ms-transform"]=i:"-webkit-transform"in t.style&&(t.style["-webkit-transform"]=i),"ie"===s&&o&&(o.parentNode.insertBefore(o,o),window.setTimeout(function(){n.refreshDefsGlobal()},n.internetExplorerRedisplayInterval))},getEventPoint:function(t,e){var o=e.createSVGPoint();return l.mouseAndTouchNormalize(t,e),o.x=t.clientX,o.y=t.clientY,o},getSvgCenterPoint:function(t,e,o){return this.createSVGPoint(t,e/2,o/2)},createSVGPoint:function(t,e,o){var n=t.createSVGPoint();return n.x=e,n.y=o,n}}},{"./utilities":7}],6:[function(t,e,o){function s(t,o){function e(t){var e={originalEvent:t=t||window.event,target:t.target||t.srcElement,type:"wheel",deltaMode:"MozMousePixelScroll"==t.type?0:1,deltaX:0,delatZ:0,preventDefault:function(){t.preventDefault?t.preventDefault():t.returnValue=!1}};return"mousewheel"==l?(e.deltaY=-.025*t.wheelDelta,t.wheelDeltaX&&(e.deltaX=-.025*t.wheelDeltaX)):e.deltaY=t.detail,o(e)}return h.push({element:t,fn:e}),e}function n(t,e,o,n){var i;i="wheel"===l?o:s(t,o),t[r](u+e,i,n?c:p)}function i(t,e,o,n){var i;i="wheel"===l?o:function(t){for(var e=0;e<h.length;e++)if(h[e].element===t)return h[e].fn;return function(){}}(t),t[a](u+e,i,n?c:p),function(t){for(var e=0;e<h.length;e++)if(h[e].element===t)return h.splice(e,1)}(t)}var r,a,l,u,h,c,p;e.exports=(h=[],p={passive:!(c={passive:!(u="")})},window.addEventListener?(r="addEventListener",a="removeEventListener"):(r="attachEvent",a="detachEvent",u="on"),l="onwheel"in document.createElement("div")?"wheel":void 0!==document.onmousewheel?"mousewheel":"DOMMouseScroll",{on:function(t,e,o){n(t,l,e,o),"DOMMouseScroll"==l&&n(t,"MozMousePixelScroll",e,o)},off:function(t,e,o){i(t,l,e,o),"DOMMouseScroll"==l&&i(t,"MozMousePixelScroll",e,o)}})},{}],7:[function(t,e,o){function n(e){return function(t){window.setTimeout(t,e)}}e.exports={extend:function(t,e){for(var o in t=t||{},e)this.isObject(e[o])?t[o]=this.extend(t[o],e[o]):t[o]=e[o];return t},isElement:function(t){return t instanceof HTMLElement||t instanceof SVGElement||t instanceof SVGSVGElement||t&&"object"==typeof t&&null!==t&&1===t.nodeType&&"string"==typeof t.nodeName},isObject:function(t){return"[object Object]"===Object.prototype.toString.call(t)},isNumber:function(t){return!isNaN(parseFloat(t))&&isFinite(t)},getSvg:function(t){var e,o;if(this.isElement(t))e=t;else{if(!("string"==typeof t||t instanceof String))throw new Error("Provided selector is not an HTML object nor String");if(!(e=document.querySelector(t)))throw new Error("Provided selector did not find any elements. Selector: "+t)}if("svg"===e.tagName.toLowerCase())o=e;else if("object"===e.tagName.toLowerCase())o=e.contentDocument.documentElement;else{if("embed"!==e.tagName.toLowerCase())throw"img"===e.tagName.toLowerCase()?new Error('Cannot script an SVG in an "img" element. Please use an "object" element or an in-line SVG.'):new Error("Cannot get SVG.");o=e.getSVGDocument().documentElement}return o},proxy:function(t,e){return function(){return t.apply(e,arguments)}},getType:function(t){return Object.prototype.toString.apply(t).replace(/^\[object\s/,"").replace(/\]$/,"")},mouseAndTouchNormalize:function(t,e){if(void 0===t.clientX||null===t.clientX)if(t.clientX=0,void(t.clientY=0)!==t.touches&&t.touches.length){if(void 0!==t.touches[0].clientX)t.clientX=t.touches[0].clientX,t.clientY=t.touches[0].clientY;else if(void 0!==t.touches[0].pageX){var o=e.getBoundingClientRect();t.clientX=t.touches[0].pageX-o.left,t.clientY=t.touches[0].pageY-o.top}}else void 0!==t.originalEvent&&void 0!==t.originalEvent.clientX&&(t.clientX=t.originalEvent.clientX,t.clientY=t.originalEvent.clientY)},isDblClick:function(t,e){if(2===t.detail)return!0;if(null==e)return!1;var o=t.timeStamp-e.timeStamp,n=Math.sqrt(Math.pow(t.clientX-e.clientX,2)+Math.pow(t.clientY-e.clientY,2));return o<250&&n<10},now:Date.now||function(){return(new Date).getTime()},throttle:function(o,n,i){var s,r,a,l=this,u=null,h=0;i=i||{};function c(){h=!1===i.leading?0:l.now(),u=null,a=o.apply(s,r),u||(s=r=null)}return function(){var t=l.now();h||!1!==i.leading||(h=t);var e=n-(t-h);return s=this,r=arguments,e<=0||n<e?(clearTimeout(u),u=null,h=t,a=o.apply(s,r),u||(s=r=null)):u||!1===i.trailing||(u=setTimeout(c,e)),a}},createRequestAnimationFrame:function(t){var e=null;return"auto"!==t&&t<60&&1<t&&(e=Math.floor(1e3/t)),null===e?window.requestAnimationFrame||n(33):n(e)}}},{}]},{},[3]);
|
|
@@ -145,8 +145,6 @@ def _watch_html(
|
|
|
145
145
|
filter_completed=False,
|
|
146
146
|
comparison=None,
|
|
147
147
|
):
|
|
148
|
-
# Keep the SVG as the page's main content so browser zoom behavior matches
|
|
149
|
-
# the original watcher experience (the graph scales, not just footer text).
|
|
150
148
|
# {{{ Build links for the other preview views
|
|
151
149
|
links = []
|
|
152
150
|
diff_query = (
|
|
@@ -184,13 +182,37 @@ def _watch_html(
|
|
|
184
182
|
+ comparison.commit[:12]
|
|
185
183
|
+ ")"
|
|
186
184
|
)
|
|
185
|
+
# The graph viewport fills the window above the footer links. Pan/zoom
|
|
186
|
+
# (wheel, drag, toolbar, box zoom) is handled inside the embedded SVG by
|
|
187
|
+
# wgrph_view.js, so it never changes the browser zoom level.
|
|
187
188
|
return (
|
|
188
|
-
"<html><
|
|
189
|
-
"
|
|
189
|
+
"<html><head><style>"
|
|
190
|
+
"html,body{height:100%;margin:0;overflow:hidden;}"
|
|
191
|
+
"body{display:flex;flex-direction:column;}"
|
|
192
|
+
"#graph-area{position:relative;flex:1 1 auto;min-height:0;}"
|
|
193
|
+
"#svg-view{display:block;width:100%;height:100%;}"
|
|
194
|
+
"#wgrph-toolbar{position:absolute;top:6px;right:6px;display:flex;"
|
|
195
|
+
"gap:2px;}"
|
|
196
|
+
"#wgrph-toolbar button{font-size:15px;min-width:2em;cursor:pointer;"
|
|
197
|
+
"background:#f4f4f4;border:1px solid #aaa;border-radius:3px;}"
|
|
198
|
+
"#wgrph-toolbar button.active{background:#cde;border-color:#468;}"
|
|
199
|
+
"</style></head><body>"
|
|
200
|
+
"<div id='graph-area'>"
|
|
201
|
+
"<embed id='svg-view' type='image/svg+xml'"
|
|
190
202
|
f" src='{svg_url}'/>"
|
|
203
|
+
"<div id='wgrph-toolbar'>"
|
|
204
|
+
"<button id='wgrph-home' title='Home: fit whole graph'>⌂"
|
|
205
|
+
"</button>"
|
|
206
|
+
"<button id='wgrph-zoom-in' title='Zoom in'>+</button>"
|
|
207
|
+
"<button id='wgrph-zoom-out' title='Zoom out'>−</button>"
|
|
208
|
+
"<button id='wgrph-box-zoom' title='Zoom to rectangle (Esc to exit)'>"
|
|
209
|
+
"□</button>"
|
|
210
|
+
"</div></div>"
|
|
191
211
|
"<p style='margin:0.4em 0.8em;font-family:sans-serif;font-size:13px;'>"
|
|
192
212
|
f"{footer_html}"
|
|
193
213
|
"</p>"
|
|
214
|
+
"<script src='/svg-pan-zoom.min.js'></script>"
|
|
215
|
+
"<script src='/wgrph_view.js'></script>"
|
|
194
216
|
"</body></html>"
|
|
195
217
|
)
|
|
196
218
|
|
|
@@ -305,10 +327,12 @@ def build_graph(
|
|
|
305
327
|
for index, child in enumerate(list(group)):
|
|
306
328
|
if child.tag != f"{namespace}text" or child.text is None:
|
|
307
329
|
continue
|
|
308
|
-
|
|
330
|
+
marker_index = child.text.find(link_marker)
|
|
331
|
+
if marker_index < 0:
|
|
309
332
|
continue
|
|
310
|
-
|
|
311
|
-
child.text
|
|
333
|
+
prefix = child.text[:marker_index]
|
|
334
|
+
task_name = child.text[marker_index + len(link_marker) :]
|
|
335
|
+
child.text = prefix + task_name
|
|
312
336
|
link = ET.Element(f"{namespace}a")
|
|
313
337
|
link.set(
|
|
314
338
|
f"{{{xlink_ns}}}href", f"/?t={urllib.parse.quote(task_name)}"
|
|
@@ -705,6 +729,13 @@ class FlowchartPreviewServer:
|
|
|
705
729
|
"image/svg+xml; charset=utf-8",
|
|
706
730
|
)
|
|
707
731
|
return
|
|
732
|
+
if parsed.path in ("/svg-pan-zoom.min.js", "/wgrph_view.js"):
|
|
733
|
+
_send_preview_response(
|
|
734
|
+
self,
|
|
735
|
+
(Path(__file__).parent / parsed.path[1:]).read_bytes(),
|
|
736
|
+
"text/javascript; charset=utf-8",
|
|
737
|
+
)
|
|
738
|
+
return
|
|
708
739
|
|
|
709
740
|
if parsed.path != "/" and parsed.path != "/index.html":
|
|
710
741
|
self.send_error(404)
|
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
// Matplotlib-style pan/zoom for the wgrph preview page.
|
|
2
|
+
//
|
|
3
|
+
// svg-pan-zoom (vendored next to this file) handles wheel zoom and drag pan
|
|
4
|
+
// inside the embedded SVG. This script adds the "home" view (whole graph fit
|
|
5
|
+
// and pinned to the top), a zoom-to-rectangle mode, a guard so that a drag
|
|
6
|
+
// ending on a task link does not navigate, and restoration of the current
|
|
7
|
+
// view when the watcher swaps in a rebuilt SVG.
|
|
8
|
+
(function () {
|
|
9
|
+
const embed = document.getElementById('svg-view');
|
|
10
|
+
const boxButton = document.getElementById('wgrph-box-zoom');
|
|
11
|
+
// Last view seen on this page; null until the first SVG is shown, so only
|
|
12
|
+
// live reloads (same page, new src) restore it.
|
|
13
|
+
let savedView = null;
|
|
14
|
+
let boxMode = false;
|
|
15
|
+
let panZoom = null;
|
|
16
|
+
let initializedDoc = null;
|
|
17
|
+
|
|
18
|
+
function saveView() {
|
|
19
|
+
savedView = {
|
|
20
|
+
realZoom: panZoom.getSizes().realZoom,
|
|
21
|
+
pan: panZoom.getPan(),
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function home() {
|
|
26
|
+
// Fit the whole graph, pinned to the top and centered horizontally.
|
|
27
|
+
panZoom.resize();
|
|
28
|
+
panZoom.reset();
|
|
29
|
+
const sizes = panZoom.getSizes();
|
|
30
|
+
panZoom.panBy({
|
|
31
|
+
x: (sizes.width - sizes.viewBox.width * sizes.realZoom) / 2,
|
|
32
|
+
y: 0,
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function setBoxMode(enabled) {
|
|
37
|
+
boxMode = enabled;
|
|
38
|
+
boxButton.classList.toggle('active', enabled);
|
|
39
|
+
if (panZoom === null) {
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
if (enabled) {
|
|
43
|
+
panZoom.disablePan();
|
|
44
|
+
} else {
|
|
45
|
+
panZoom.enablePan();
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function init() {
|
|
50
|
+
const svgDoc = embed.getSVGDocument();
|
|
51
|
+
if (
|
|
52
|
+
svgDoc === null ||
|
|
53
|
+
svgDoc.readyState !== 'complete' ||
|
|
54
|
+
svgDoc.documentElement === null ||
|
|
55
|
+
svgDoc === initializedDoc
|
|
56
|
+
) {
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
initializedDoc = svgDoc;
|
|
60
|
+
const svg = svgDoc.documentElement;
|
|
61
|
+
// Let the SVG fill the embed box; svg-pan-zoom then moves the viewBox
|
|
62
|
+
// scaling onto its viewport group.
|
|
63
|
+
svg.setAttribute('width', '100%');
|
|
64
|
+
svg.setAttribute('height', '100%');
|
|
65
|
+
panZoom = svgPanZoom(embed, {
|
|
66
|
+
fit: true,
|
|
67
|
+
center: false,
|
|
68
|
+
dblClickZoomEnabled: false,
|
|
69
|
+
zoomScaleSensitivity: 0.2,
|
|
70
|
+
minZoom: 0.1,
|
|
71
|
+
maxZoom: 50,
|
|
72
|
+
onZoom: saveView,
|
|
73
|
+
onPan: saveView,
|
|
74
|
+
});
|
|
75
|
+
window.wgrphPanZoom = panZoom;
|
|
76
|
+
if (savedView === null) {
|
|
77
|
+
home();
|
|
78
|
+
} else {
|
|
79
|
+
const restore = savedView;
|
|
80
|
+
panZoom.zoom(
|
|
81
|
+
(restore.realZoom / panZoom.getSizes().realZoom) * panZoom.getZoom()
|
|
82
|
+
);
|
|
83
|
+
panZoom.pan(restore.pan);
|
|
84
|
+
}
|
|
85
|
+
saveView();
|
|
86
|
+
setBoxMode(boxMode);
|
|
87
|
+
|
|
88
|
+
// {{{ Suppress link clicks that end a pan or a box selection
|
|
89
|
+
let downPoint = null;
|
|
90
|
+
let dragged = false;
|
|
91
|
+
svgDoc.addEventListener(
|
|
92
|
+
'mousedown',
|
|
93
|
+
function (evt) {
|
|
94
|
+
downPoint = { x: evt.clientX, y: evt.clientY };
|
|
95
|
+
dragged = false;
|
|
96
|
+
},
|
|
97
|
+
true
|
|
98
|
+
);
|
|
99
|
+
svgDoc.addEventListener(
|
|
100
|
+
'mousemove',
|
|
101
|
+
function (evt) {
|
|
102
|
+
if (
|
|
103
|
+
downPoint !== null &&
|
|
104
|
+
Math.hypot(evt.clientX - downPoint.x, evt.clientY - downPoint.y) > 4
|
|
105
|
+
) {
|
|
106
|
+
dragged = true;
|
|
107
|
+
}
|
|
108
|
+
},
|
|
109
|
+
true
|
|
110
|
+
);
|
|
111
|
+
svgDoc.addEventListener(
|
|
112
|
+
'click',
|
|
113
|
+
function (evt) {
|
|
114
|
+
if (dragged || boxMode) {
|
|
115
|
+
evt.preventDefault();
|
|
116
|
+
evt.stopPropagation();
|
|
117
|
+
}
|
|
118
|
+
downPoint = null;
|
|
119
|
+
dragged = false;
|
|
120
|
+
},
|
|
121
|
+
true
|
|
122
|
+
);
|
|
123
|
+
// }}}
|
|
124
|
+
|
|
125
|
+
// {{{ Zoom-to-rectangle mode
|
|
126
|
+
let boxStart = null;
|
|
127
|
+
let boxRect = null;
|
|
128
|
+
svg.addEventListener('mousedown', function (evt) {
|
|
129
|
+
if (!boxMode || evt.button !== 0) {
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
boxStart = { x: evt.clientX, y: evt.clientY };
|
|
133
|
+
// The rectangle lives on the root <svg>, outside the pan/zoom
|
|
134
|
+
// viewport group, so its coordinates are plain screen pixels.
|
|
135
|
+
boxRect = svgDoc.createElementNS('http://www.w3.org/2000/svg', 'rect');
|
|
136
|
+
boxRect.setAttribute('fill', 'rgba(70,130,180,0.15)');
|
|
137
|
+
boxRect.setAttribute('stroke', 'steelblue');
|
|
138
|
+
boxRect.setAttribute('stroke-dasharray', '4 3');
|
|
139
|
+
boxRect.setAttribute('pointer-events', 'none');
|
|
140
|
+
svg.appendChild(boxRect);
|
|
141
|
+
});
|
|
142
|
+
svg.addEventListener('mousemove', function (evt) {
|
|
143
|
+
if (boxStart === null) {
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
146
|
+
boxRect.setAttribute('x', Math.min(boxStart.x, evt.clientX));
|
|
147
|
+
boxRect.setAttribute('y', Math.min(boxStart.y, evt.clientY));
|
|
148
|
+
boxRect.setAttribute('width', Math.abs(evt.clientX - boxStart.x));
|
|
149
|
+
boxRect.setAttribute('height', Math.abs(evt.clientY - boxStart.y));
|
|
150
|
+
});
|
|
151
|
+
svgDoc.addEventListener('mouseup', function (evt) {
|
|
152
|
+
if (boxStart === null) {
|
|
153
|
+
return;
|
|
154
|
+
}
|
|
155
|
+
const width = Math.abs(evt.clientX - boxStart.x);
|
|
156
|
+
const height = Math.abs(evt.clientY - boxStart.y);
|
|
157
|
+
const center = {
|
|
158
|
+
x: (evt.clientX + boxStart.x) / 2,
|
|
159
|
+
y: (evt.clientY + boxStart.y) / 2,
|
|
160
|
+
};
|
|
161
|
+
boxRect.remove();
|
|
162
|
+
boxRect = null;
|
|
163
|
+
boxStart = null;
|
|
164
|
+
if (width <= 5 || height <= 5) {
|
|
165
|
+
return;
|
|
166
|
+
}
|
|
167
|
+
const sizes = panZoom.getSizes();
|
|
168
|
+
panZoom.zoomAtPointBy(
|
|
169
|
+
Math.min(sizes.width / width, sizes.height / height),
|
|
170
|
+
center
|
|
171
|
+
);
|
|
172
|
+
panZoom.panBy({
|
|
173
|
+
x: sizes.width / 2 - center.x,
|
|
174
|
+
y: sizes.height / 2 - center.y,
|
|
175
|
+
});
|
|
176
|
+
});
|
|
177
|
+
// }}}
|
|
178
|
+
svgDoc.addEventListener('keydown', function (evt) {
|
|
179
|
+
if (evt.key === 'Escape') {
|
|
180
|
+
setBoxMode(false);
|
|
181
|
+
}
|
|
182
|
+
});
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
embed.addEventListener('load', init);
|
|
186
|
+
// The SVG may already be loaded by the time this script runs.
|
|
187
|
+
init();
|
|
188
|
+
window.addEventListener('resize', function () {
|
|
189
|
+
if (panZoom !== null) {
|
|
190
|
+
panZoom.resize();
|
|
191
|
+
}
|
|
192
|
+
});
|
|
193
|
+
document.addEventListener('keydown', function (evt) {
|
|
194
|
+
if (evt.key === 'Escape') {
|
|
195
|
+
setBoxMode(false);
|
|
196
|
+
}
|
|
197
|
+
});
|
|
198
|
+
document.getElementById('wgrph-home').addEventListener('click', home);
|
|
199
|
+
document
|
|
200
|
+
.getElementById('wgrph-zoom-in')
|
|
201
|
+
.addEventListener('click', function () {
|
|
202
|
+
panZoom.zoomBy(1.25);
|
|
203
|
+
});
|
|
204
|
+
document
|
|
205
|
+
.getElementById('wgrph-zoom-out')
|
|
206
|
+
.addEventListener('click', function () {
|
|
207
|
+
panZoom.zoomBy(0.8);
|
|
208
|
+
});
|
|
209
|
+
boxButton.addEventListener('click', function () {
|
|
210
|
+
setBoxMode(!boxMode);
|
|
211
|
+
});
|
|
212
|
+
})();
|
|
@@ -171,8 +171,8 @@ def xore(filename):
|
|
|
171
171
|
all_contents = pickle.load(fp)
|
|
172
172
|
all_contents.set_format(format_type)
|
|
173
173
|
with open(markdownfile, "r", encoding="utf-8") as fp:
|
|
174
|
-
for
|
|
175
|
-
|
|
174
|
+
outline_lines = [j.rstrip() for j in fp]
|
|
175
|
+
all_contents.outline_in_order(outline_lines)
|
|
176
176
|
# build the text before opening the target, so that an error (e.g. a
|
|
177
177
|
# section missing from the outline) doesn't leave an empty file
|
|
178
178
|
reordered = str(all_contents)
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "pyDiffTools"
|
|
7
|
-
version = "0.1.
|
|
7
|
+
version = "0.1.33"
|
|
8
8
|
authors = [{ name = "J M Franck" }]
|
|
9
9
|
description = "Diff tools"
|
|
10
10
|
readme = "README.rst"
|
|
@@ -49,6 +49,10 @@ pydifftools = [
|
|
|
49
49
|
"comment_tags_no_comments.lua",
|
|
50
50
|
"comment_toggle.js",
|
|
51
51
|
]
|
|
52
|
+
"pydifftools.flowchart" = [
|
|
53
|
+
"svg-pan-zoom.min.js",
|
|
54
|
+
"wgrph_view.js",
|
|
55
|
+
]
|
|
52
56
|
|
|
53
57
|
[tool.black]
|
|
54
58
|
line-length = 79
|
|
@@ -1,162 +0,0 @@
|
|
|
1
|
-
from collections import OrderedDict
|
|
2
|
-
from fuzzywuzzy import process
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
class doc_contents_class(object):
|
|
6
|
-
def __init__(self, format_type="latex"):
|
|
7
|
-
self.contents = OrderedDict()
|
|
8
|
-
self.contents["header"] = ""
|
|
9
|
-
self.types = {}
|
|
10
|
-
self.types["header"] = "header"
|
|
11
|
-
self._reordering_started = False
|
|
12
|
-
self._aliases = {}
|
|
13
|
-
self._processed_titles = []
|
|
14
|
-
self.set_format(format_type)
|
|
15
|
-
|
|
16
|
-
def set_format(self, format_type):
|
|
17
|
-
if format_type == "markdown":
|
|
18
|
-
# markdown levels go deeper, so include subparagraph mapping
|
|
19
|
-
self.level_numbers = {
|
|
20
|
-
"section": 1,
|
|
21
|
-
"subsection": 2,
|
|
22
|
-
"subsubsection": 3,
|
|
23
|
-
"paragraph": 4,
|
|
24
|
-
"subparagraph": 5,
|
|
25
|
-
}
|
|
26
|
-
else:
|
|
27
|
-
# default to latex behavior
|
|
28
|
-
self.level_numbers = {
|
|
29
|
-
"section": 1,
|
|
30
|
-
"subsection": 2,
|
|
31
|
-
"subsubsection": 3,
|
|
32
|
-
"paragraph": 4,
|
|
33
|
-
"subparagraph": 5,
|
|
34
|
-
}
|
|
35
|
-
# map indentation back to section type for outline parsing
|
|
36
|
-
self.inv_prefix = {
|
|
37
|
-
(level - 1) * "\t": section
|
|
38
|
-
for section, level in self.level_numbers.items()
|
|
39
|
-
}
|
|
40
|
-
self.format_type = format_type
|
|
41
|
-
|
|
42
|
-
def start_sec(self, thistype, thistitle):
|
|
43
|
-
assert thistitle not in self.contents.keys(), (
|
|
44
|
-
"more than one section with the name:\n" + thistitle
|
|
45
|
-
)
|
|
46
|
-
self.contents[thistitle] = ""
|
|
47
|
-
self.types[thistitle] = thistype
|
|
48
|
-
print("added", thistitle)
|
|
49
|
-
|
|
50
|
-
def __setstate__(self, d):
|
|
51
|
-
"set the info from a pickle"
|
|
52
|
-
self.contents = d["contents"]
|
|
53
|
-
self.types = d["types"]
|
|
54
|
-
self._aliases = {} # doesn't exist, but still needed
|
|
55
|
-
self._reordering_started = False
|
|
56
|
-
self._processed_titles = []
|
|
57
|
-
if "format_type" in d:
|
|
58
|
-
self.set_format(d["format_type"])
|
|
59
|
-
else:
|
|
60
|
-
self.set_format("latex")
|
|
61
|
-
return
|
|
62
|
-
|
|
63
|
-
def __getstate__(self):
|
|
64
|
-
"return info for a pickle"
|
|
65
|
-
return {
|
|
66
|
-
"contents": self.contents,
|
|
67
|
-
"types": self.types,
|
|
68
|
-
"format_type": self.format_type,
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
def __iadd__(self, value):
|
|
72
|
-
self.contents[next(reversed(self.contents))] += value
|
|
73
|
-
return self
|
|
74
|
-
|
|
75
|
-
def __str__(self):
|
|
76
|
-
if len(self._processed_titles) > 0:
|
|
77
|
-
raise ValueError(
|
|
78
|
-
"the following section titles are missing from the"
|
|
79
|
-
" outline:\n\t"
|
|
80
|
-
+ "\n\t".join(self._processed_titles)
|
|
81
|
-
+ "\nEvery existing section must appear in the outline, so"
|
|
82
|
-
" that entire sections of text are never dropped by"
|
|
83
|
-
" accident. If you really want to get rid of a section,"
|
|
84
|
-
" add a new heading (e.g. 'for deletion') to the outline"
|
|
85
|
-
" and move the unwanted sections underneath it; then delete"
|
|
86
|
-
" that part of the file by hand."
|
|
87
|
-
)
|
|
88
|
-
retval = ""
|
|
89
|
-
for j in self.contents.keys():
|
|
90
|
-
if self.types[j] != "header":
|
|
91
|
-
new_name = j
|
|
92
|
-
if j in self._aliases.keys():
|
|
93
|
-
new_name = self._aliases[j]
|
|
94
|
-
if self.format_type == "markdown":
|
|
95
|
-
retval += "#" * self.level_numbers[self.types[j]]
|
|
96
|
-
retval += f" {new_name}\n"
|
|
97
|
-
else:
|
|
98
|
-
retval += f"\\{self.types[j]}{{{new_name}}}"
|
|
99
|
-
retval += f"{self.contents[j]}"
|
|
100
|
-
return retval
|
|
101
|
-
|
|
102
|
-
@property
|
|
103
|
-
def outline(self):
|
|
104
|
-
retval = []
|
|
105
|
-
for j in self.contents.keys():
|
|
106
|
-
if self.types[j] != "header":
|
|
107
|
-
indent = (self.level_numbers[self.types[j]] - 1) * "\t"
|
|
108
|
-
thistitle = (indent + "\t").join(j.split("\n"))
|
|
109
|
-
retval.append(indent + "*\t" + thistitle)
|
|
110
|
-
self._reordering_started = False
|
|
111
|
-
return "\n".join(retval)
|
|
112
|
-
|
|
113
|
-
def outline_in_order(self, thisline):
|
|
114
|
-
if not self._reordering_started:
|
|
115
|
-
self._processed_titles = [
|
|
116
|
-
j for j in self.contents.keys() if self.types[j] != "header"
|
|
117
|
-
]
|
|
118
|
-
self._reordering_started = True
|
|
119
|
-
ilevel = 0
|
|
120
|
-
spacelevel = 0
|
|
121
|
-
hitmarker = False
|
|
122
|
-
for j, thischar in enumerate(thisline):
|
|
123
|
-
if not hitmarker:
|
|
124
|
-
if thischar == " ":
|
|
125
|
-
spacelevel += 1
|
|
126
|
-
if spacelevel == 4 or thischar == "\t":
|
|
127
|
-
ilevel += 1
|
|
128
|
-
spacelevel = 0
|
|
129
|
-
elif thischar == "*":
|
|
130
|
-
hitmarker = True
|
|
131
|
-
else:
|
|
132
|
-
assert thischar in [" ", "\t"]
|
|
133
|
-
title = thisline[j + 1 :]
|
|
134
|
-
break
|
|
135
|
-
if not hitmarker:
|
|
136
|
-
raise ValueError("somehow, there wasn't a * marker!")
|
|
137
|
-
if title not in self.contents.keys():
|
|
138
|
-
best_match, match_quality = process.extractOne(
|
|
139
|
-
title, self.contents.keys()
|
|
140
|
-
)
|
|
141
|
-
yesorno = input(
|
|
142
|
-
f"didn't find\n\t{title}\namong the existing sections."
|
|
143
|
-
f" Is it a renamed version of\n\t{best_match}\n?"
|
|
144
|
-
" say y to rename, or n to add it as a new, empty section: "
|
|
145
|
-
)
|
|
146
|
-
if yesorno == "y":
|
|
147
|
-
self._aliases[best_match] = title # will be replaced later
|
|
148
|
-
title = best_match
|
|
149
|
-
elif yesorno == "n":
|
|
150
|
-
# a new section, so it isn't on the list of titles to place
|
|
151
|
-
self.contents[title] = ""
|
|
152
|
-
self._processed_titles.append(title)
|
|
153
|
-
else:
|
|
154
|
-
raise ValueError("expected y or n")
|
|
155
|
-
if title not in self._processed_titles:
|
|
156
|
-
raise ValueError(
|
|
157
|
-
f"the section\n\t{title}\nappears more than once in the"
|
|
158
|
-
" outline"
|
|
159
|
-
)
|
|
160
|
-
self.contents.move_to_end(title)
|
|
161
|
-
self._processed_titles.remove(title)
|
|
162
|
-
self.types[title] = self.inv_prefix[ilevel * "\t"]
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{pydifftools-0.1.31 → pydifftools-0.1.33}/example_notebook/project1/.jupyter_cache/__version__.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{pydifftools-0.1.31 → pydifftools-0.1.33}/example_notebook/project1/.jupyter_cache/global.db
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{pydifftools-0.1.31 → pydifftools-0.1.33}/example_notebook/project1/subproject1/end_vs_he_sketch.jpg
RENAMED
|
File without changes
|
{pydifftools-0.1.31 → pydifftools-0.1.33}/example_notebook/project1/subproject1/independent.qmd
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{pydifftools-0.1.31 → pydifftools-0.1.33}/example_notebook/project1/subproject1/test_include.qmd
RENAMED
|
File without changes
|
{pydifftools-0.1.31 → pydifftools-0.1.33}/example_notebook/project1/subproject1/tryforerror.qmd
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|