Codebase list anki / 5443a0d
New upstream version 2.1.0+dfsg~b16 Julian Gilbey 6 years ago
95 changed file(s) with 516 addition(s) and 361 deletion(s). Raw diff Collapse all Expand all
0 Anki's logo is copyright Alex Fraser, and is licensed under the AGPL3
1 like the rest of Anki's code, but with extra provisions to allow more
2 liberal use of the logo under limited conditions.
0 Anki's logo is copyright Alex Fraser, and is licensed under the AGPL3 like the
1 rest of Anki's code.
32
4 Under the following conditions, Anki's logo may be included in blogs,
5 newspaper articles, books, videos and other such material about Anki.
3 The logo is also available under a limited alternative license for inclusion
4 in books, blogs, videos and so on. If the following conditions are met, you
5 may use the logo in your work without the need to license your work under an
6 AGPL3-compatible license:
67
78 * The logo must be used to refer to Anki, AnkiMobile or AnkiDroid,
89 and a link to https://apps.ankiweb.net must be provided. When your
910 content is focused specifically on AnkiDroid, a link to
1011 https://play.google.com/store/apps/details?id=com.ichi2.anki&hl=en
1112 may be provided instead of the first link.
12 * The branding of your website or publication must be more prominent
13 than the Anki logo, to make it clear that the text/video/etc you
13 * The work must make it clear that the text/video/etc you
1414 are publishing is your own content and not something originating
1515 from the Anki project.
1616 * The logo must be used unmodified - no cropping, changing of colours
66 if sys.version_info[0] < 3 or sys.version_info[1] < 6:
77 raise Exception("Anki requires Python 3.6+")
88
9 version="2.1.0beta15" # build scripts grep this line, so preserve formatting
9 if sys.getfilesystemencoding().lower() in ("ascii", "ansi_x3.4-1968"):
10 raise Exception("Anki requires a UTF-8 locale.")
11
12 version="2.1.0beta16" # build scripts grep this line, so preserve formatting
1013 from anki.storage import Collection
1114 __all__ = ["Collection"]
4444
4545 key = _("Cards in Plain Text")
4646 ext = ".txt"
47 hideTags = True
4847
4948 def __init__(self, col):
5049 Exporter.__init__(self, col)
7069
7170 key = _("Notes in Plain Text")
7271 ext = ".txt"
72 includeTags = True
7373
7474 def __init__(self, col):
7575 Exporter.__init__(self, col)
7676 self.includeID = False
77 self.includeTags = True
7877
7978 def doExport(self, file):
8079 cardIds = self.cardIds()
106105
107106 key = _("Anki 2.0 Deck")
108107 ext = ".anki2"
108 includeSched = False
109 includeMedia = True
109110
110111 def __init__(self, col):
111112 Exporter.__init__(self, col)
112 self.includeSched = False
113 self.includeMedia = True
114113
115114 def exportInto(self, path):
116115 # create a new collection at the target
257256 def exportInto(self, path):
258257 # open a zip file
259258 z = zipfile.ZipFile(path, "w", zipfile.ZIP_DEFLATED, allowZip64=True)
260 # if all decks and scheduling included, full export
261 if self.includeSched and not self.did:
262 media = self.exportVerbatim(z)
263 else:
264 # otherwise, filter
265 media = self.exportFiltered(z, path)
259 media = self.doExport(z, path)
266260 # media map
267261 z.writestr("media", json.dumps(media))
268262 z.close()
269263
270 def exportFiltered(self, z, path):
264 def doExport(self, z, path):
271265 # export into the anki2 file
272266 colfile = path.replace(".apkg", ".anki2")
273267 AnkiExporter.exportInto(self, colfile)
284278 shutil.rmtree(path.replace(".apkg", ".media"))
285279 return media
286280
287 def exportVerbatim(self, z):
288 # close our deck & write it into the zip file, and reopen
289 self.count = self.col.cardCount()
290 self.col.close()
291 z.write(self.col.path, "collection.anki2")
292 self.col.reopen()
293 # copy all media
294 if not self.includeMedia:
295 return {}
296 mdir = self.col.media.dir()
297 return self._exportMedia(z, os.listdir(mdir), mdir)
298
299281 def _exportMedia(self, z, files, fdir):
300282 media = {}
301283 for c, file in enumerate(files):
318300 # is zipped up
319301 pass
320302
303 # Collection package
304 ######################################################################
305
306 class AnkiCollectionPackageExporter(AnkiPackageExporter):
307
308 key = _("Anki Collection Package")
309 ext = ".colpkg"
310 verbatim = True
311 includeSched = None
312
313 def __init__(self, col):
314 AnkiPackageExporter.__init__(self, col)
315
316 def doExport(self, z, path):
317 # close our deck & write it into the zip file, and reopen
318 self.count = self.col.cardCount()
319 self.col.close()
320 z.write(self.col.path, "collection.anki2")
321 self.col.reopen()
322 # copy all media
323 if not self.includeMedia:
324 return {}
325 mdir = self.col.media.dir()
326 return self._exportMedia(z, os.listdir(mdir), mdir)
327
321328 # Export modules
322329 ##########################################################################
323330
325332 def id(obj):
326333 return ("%s (*%s)" % (obj.key, obj.ext), obj)
327334 exps = [
335 id(AnkiCollectionPackageExporter),
328336 id(AnkiPackageExporter),
329337 id(TextNoteExporter),
330338 id(TextCardExporter),
393393 else:
394394 # wildcard
395395 ids = set()
396 # should use re.escape in the future
397 val = val.replace("*", ".*")
398 val = val.replace("+", "\\+")
396 val = re.escape(val).replace(r"\*", ".*")
399397 for d in self.col.decks.all():
400398 if re.match("(?i)"+val, d['name']):
401399 ids.update(dids(d['id']))
1111
1212 Importers = (
1313 (_("Text separated by tabs or semicolons (*)"), TextImporter),
14 (_("Packaged Anki Deck (*.apkg *.zip)"), AnkiPackageImporter),
14 (_("Packaged Anki Deck/Collection (*.apkg *.colpkg *.zip)"), AnkiPackageImporter),
1515 (_("Mnemosyne 2.0 Deck (*.db)"), MnemosyneImporter),
1616 (_("Supermemo XML export (*.xml)"), SupermemoXmlImporter),
1717 (_("Pauker 1.8 Lesson (*.pau.gz)"), PaukerImporter),
196196 return m.group(1)
197197 for ord in ords:
198198 s = re.sub(clozeReg%ord, qrepl, string)
199 s = re.sub(clozeReg%".+?", "\\1", s)
199 s = re.sub(clozeReg%".+?", "\\2", s)
200200 strings.append(s)
201201 strings.append(re.sub(clozeReg%".+?", arepl, string))
202202 return strings
982982
983983 def _dynOrder(self, o, l):
984984 if o == DYN_OLDEST:
985 t = "c.mod"
985 t = "(select max(id) from revlog where cid=c.id)"
986986 elif o == DYN_RANDOM:
987987 t = "random()"
988988 elif o == DYN_SMALLINT:
7878 ##########################################################################
7979
8080 mplayerCmd = ["mplayer", "-really-quiet", "-noautosub"]
81 if isWin:
82 mplayerCmd += ["-ao", "win32"]
8381
8482 # Mplayer in slave mode
8583 ##########################################################################
33 from anki.template import furigana; furigana.install()
44 from anki.template import hint; hint.install()
55
6 clozeReg = r"(?s)\{\{c%s::(.*?)(::(.*?))?\}\}"
6 clozeReg = r"(?si)\{\{(c)%s::(.*?)(::(.*?))?\}\}"
77
88 modifiers = {}
99 def modifier(symbol):
177177 # hook-based field modifier
178178 mod, extra = re.search("^(.*?)(?:\((.*)\))?$", mod).groups()
179179 txt = runFilter('fmod_' + mod, txt or '', extra or '', context,
180 tag, tag_name);
180 tag, tag_name)
181181 if txt is None:
182182 return '{unknown field %s}' % tag_name
183183 return txt
186186 reg = clozeReg
187187 if not re.search(reg%ord, txt):
188188 return ""
189 txt = self._removeFormattingFromMathjax(txt, ord)
189190 def repl(m):
190191 # replace chosen cloze with type
191192 if type == "q":
192 if m.group(3):
193 return "<span class=cloze>[%s]</span>" % m.group(3)
193 if m.group(4):
194 buf = "[%s]" % m.group(4)
194195 else:
195 return "<span class=cloze>[...]</span>"
196 buf = "[...]"
196197 else:
197 return "<span class=cloze>%s</span>" % m.group(1)
198 buf = m.group(2)
199 # uppercase = no formatting
200 if m.group(1) == "c":
201 buf = "<span class=cloze>%s</span>" % buf
202 return buf
198203 txt = re.sub(reg%ord, repl, txt)
199204 # and display other clozes normally
200 return re.sub(reg%"\d+", "\\1", txt)
205 return re.sub(reg%"\d+", "\\2", txt)
206
207 # look for clozes wrapped in mathjax, and change {{cx to {{Cx
208 def _removeFormattingFromMathjax(self, txt, ord):
209 regex = r"(\\[([]).*?"+(clozeReg%ord)+r".*?(\\[\])])"
210 def repl(m):
211 return m.group(0).replace("{{c", "{{C")
212 txt = re.sub(regex, repl, txt)
213 return txt
201214
202215 @modifier('=')
203216 def render_delimiter(self, tag_name=None, context=None):
3737
3838 from anki.utils import checksum
3939
40 # Dialog manager - manages non-modal windows
40 # Dialog manager
4141 ##########################################################################
42 # ensures only one copy of the window is open at once, and provides
43 # a way for dialogs to clean up asynchronously when collection closes
44
45 # to integrate a new window:
46 # - add it to _dialogs
47 # - define close behaviour, by either:
48 # -- setting silentlyClose=True to have it close immediately
49 # -- define a closeWithCallback() method
50 # - have the window opened via aqt.dialogs.open(<name>, self)
51
52 #- make preferences modal? cmd+q does wrong thing
53
54
55 from aqt import addcards, browser, editcurrent, stats, about, \
56 preferences
4257
4358 class DialogManager:
4459
45 def __init__(self):
46 from aqt import addcards, browser, editcurrent, stats, about
47 self._dialogs = {
48 "AddCards": [addcards.AddCards, None],
49 "Browser": [browser.Browser, None],
50 "EditCurrent": [editcurrent.EditCurrent, None],
51 "DeckStats": [stats.DeckStats, None],
52 "About": [about.show, None],
53 }
60 _dialogs = {
61 "AddCards": [addcards.AddCards, None],
62 "Browser": [browser.Browser, None],
63 "EditCurrent": [editcurrent.EditCurrent, None],
64 "DeckStats": [stats.DeckStats, None],
65 "About": [about.show, None],
66 "Preferences": [preferences.Preferences, None],
67 }
5468
5569 def open(self, name, *args):
5670 (creator, instance) = self._dialogs[name]
88102 # still waiting for others to close
89103 pass
90104
91 instance.closeWithCallback(callback)
105 if getattr(instance, "silentlyClose", False):
106 instance.close()
107 callback()
108 else:
109 instance.closeWithCallback(callback)
92110
93111 return True
94112
107107 meta = self.addonMeta(sid)
108108 base = self.addonsFolder(sid)
109109 if os.path.exists(base):
110 self.backupUserFiles(sid)
110111 self.deleteAddon(sid)
111112
113 os.mkdir(base)
114 self.restoreUserFiles(sid)
115
112116 # extract
113 os.mkdir(base)
114117 for n in z.namelist():
115118 if n.endswith("/"):
116119 # folder; ignore
117120 continue
118 # write
121
122 path = os.path.join(base, n)
123 # skip existing user files
124 if os.path.exists(path) and n.startswith("user_files/"):
125 continue
119126 z.extract(n, base)
120127
121128 # update metadata
139146 errs.append(_("Error downloading %(id)s: %(error)s") % dict(id=n, error=ret[1]))
140147 continue
141148 data, fname = ret
149 fname = fname.replace("_", " ")
142150 self.install(str(n), data, fname)
143151 name = os.path.splitext(fname)[0]
144152 log.append(_("Downloaded %(fname)s" % dict(fname=name)))
235243 meta = self.addonMeta(addon)
236244 meta['config'] = conf
237245 self.writeAddonMeta(addon, meta)
246
247 # user_files
248 ######################################################################
249
250 def _userFilesPath(self, sid):
251 return os.path.join(self.addonsFolder(sid), "user_files")
252
253 def _userFilesBackupPath(self):
254 return os.path.join(self.addonsFolder(), "files_backup")
255
256 def backupUserFiles(self, sid):
257 p = self._userFilesPath(sid)
258 if os.path.exists(p):
259 os.rename(p, self._userFilesBackupPath())
260
261 def restoreUserFiles(self, sid):
262 p = self._userFilesPath(sid)
263 bp = self._userFilesBackupPath()
264 # did we back up userFiles?
265 if not os.path.exists(bp):
266 return
267 os.rename(bp, p)
238268
239269 # Add-ons Dialog
240270 ######################################################################
1818 saveHeader, restoreHeader, saveState, restoreState, applyStyles, getTag, \
1919 showInfo, askUser, tooltip, openHelp, showWarning, shortcut, mungeQA, \
2020 getOnlyText, MenuList, SubMenu
21 from anki.hooks import runHook, addHook, remHook
21 from anki.hooks import runHook, addHook, remHook, runFilter
2222 from aqt.webview import AnkiWebView
2323 from anki.consts import *
2424 from anki.sound import playFromText, clearAudioQueue
855855 def _modelTree(self, root):
856856 for m in sorted(self.col.models.all(), key=itemgetter("name")):
857857 mitem = self.CallbackItem(
858 root, m['name'], lambda m=m: self.setFilter("mid", str(m['id'])))
858 root, m['name'], lambda m=m: self.setFilter("note", m['name']))
859859 mitem.setIcon(0, QIcon(":/icons/notetype.svg"))
860860
861861 # Filter tree
892892 txt += a + ":"
893893 else:
894894 txt += a
895 if " " in txt or "(" in txt or ")" in txt:
896 txt = '"%s"' % txt
895 for chr in "  ()":
896 if chr in txt:
897 txt = '"%s"' % txt
898 break
897899 items.append(txt)
898900 txt = ""
899901 txt = " ".join(items)
10021004 for nt in sorted(self.col.models.all(), key=lambda nt: nt['name'].lower()):
10031005 # no sub menu if it's a single template
10041006 if len(nt['tmpls']) == 1:
1005 noteTypes.addItem(nt['name'], self._filterFunc("mid", str(nt['id'])))
1007 noteTypes.addItem(nt['name'], self._filterFunc("note", nt['name']))
10061008 else:
10071009 subm = noteTypes.addMenu(nt['name'])
10081010
1009 subm.addItem(_("All Card Types"), self._filterFunc("mid", str(nt['id'])))
1011 subm.addItem(_("All Card Types"), self._filterFunc("note", nt['name']))
10101012 subm.addSeparator()
10111013
10121014 # add templates
10131015 for c, tmpl in enumerate(nt['tmpls']):
10141016 name = _("%(n)d: %(name)s") % dict(n=c+1, name=tmpl['name'])
10151017 subm.addItem(name, self._filterFunc(
1016 "mid", str(nt['id']), "card", str(c+1)))
1018 "note", nt['name'], "card", str(c+1)))
10171019
10181020 m.addChild(noteTypes.chunked())
10191021 return m
12141216 self._previewWindow.setWindowTitle(_("Preview"))
12151217
12161218 self._previewWindow.finished.connect(self._onPreviewFinished)
1219 self._previewWindow.silentlyClose = True
12171220 vbox = QVBoxLayout()
12181221 vbox.setContentsMargins(0,0,0,0)
12191222 self._previewWeb = AnkiWebView()
12661269 self._renderPreview()
12671270 else:
12681271 self.editor.saveNow(lambda: self._moveCur(QAbstractItemView.MoveUp))
1269 self._updatePreviewButtons()
12701272
12711273 def _onPreviewNext(self):
12721274 if self._previewState == "question":
12741276 self._renderPreview()
12751277 else:
12761278 self.editor.saveNow(lambda: self._moveCur(QAbstractItemView.MoveDown))
1277 self._updatePreviewButtons()
12781279
12791280 def _onReplayAudio(self):
12801281 self.mw.reviewer.replayAudio(self)
13291330 if not self._previewWindow:
13301331 return
13311332 c = self.card
1332 self._updatePreviewButtons()
13331333 func = "_showQuestion"
13341334 if not c or not self.singleCard:
13351335 txt = _("(please select 1 card)")
13531353 playFromText(txt)
13541354
13551355 txt = mungeQA(self.col, txt)
1356
1356 txt = runFilter("prepareQA", txt, c,
1357 "preview"+self._previewState.capitalize())
1358
1359 self._updatePreviewButtons()
13571360 self._previewWeb.eval(
13581361 f"{func}({json.dumps(txt)},'{bodyclass}');")
13591362
1414 from anki.utils import isMac, isWin, joinFields
1515 from aqt.webview import AnkiWebView
1616 import json
17 from anki.hooks import runFilter
18
1719
1820 class CardLayout(QDialog):
1921
295297 c = self.card
296298 ti = self.maybeTextInput
297299 bodyclass="card card%d" % (c.ord+1)
300
298301 q = ti(mungeQA(self.mw.col, c.q(reload=True)))
302 q = runFilter("prepareQA", q, c, "clayoutQuestion")
303
299304 a = ti(mungeQA(self.mw.col, c.a()), type='a')
305 a = runFilter("prepareQA", a, c, "clayoutAnswer")
300306
301307 # use _showAnswer to avoid the longer delay
302308 self.pform.frontWeb.eval("_showAnswer(%s,'%s');" % (json.dumps(q), bodyclass))
184184 ("Ctrl+T, T", self.insertLatex),
185185 ("Ctrl+T, E", self.insertLatexEqn),
186186 ("Ctrl+T, M", self.insertLatexMathEnv),
187 ("Ctrl+M, M", self.insertMathjaxInline),
188 ("Ctrl+M, E", self.insertMathjaxBlock),
187189 ("Ctrl+Shift+X", self.onHtmlEdit),
188190 ("Ctrl+Shift+T", self.onFocusTags)
189191 ]
258260 print("uncaught cmd", cmd)
259261
260262 def mungeHTML(self, txt):
261 if txt == "<br>":
262 txt = ""
263 txt = re.sub(r"<br>$", "", txt)
263264 return txt
264265
265266 # Setting/unsetting the current note
646647 def onPaste(self):
647648 self.web.onPaste()
648649
650 def onCutOrCopy(self):
651 self.web.flagAnkiText()
652
649653 # Advanced menu
650654 ######################################################################
651655
652656 def onAdvanced(self):
653657 m = QMenu(self.mw)
658 a = m.addAction(_("MathJax inline"))
659 a.triggered.connect(self.insertMathjaxInline)
660 a = m.addAction(_("MathJax block"))
661 a.triggered.connect(self.insertMathjaxBlock)
654662 a = m.addAction(_("LaTeX"))
655663 a.triggered.connect(self.insertLatex)
656664 a = m.addAction(_("LaTeX equation"))
672680
673681 def insertLatexMathEnv(self):
674682 self.web.eval("wrap('[$$]', '[/$$]');")
683
684 def insertMathjaxInline(self):
685 self.web.eval("wrap('\\\\(', '\\\\)');")
686
687 def insertMathjaxBlock(self):
688 self.web.eval("wrap('\\\\[', '\\\\]');")
675689
676690 # Links from HTML
677691 ######################################################################
693707 more=onAdvanced,
694708 dupes=showDupes,
695709 paste=onPaste,
710 cutOrCopy=onCutOrCopy,
696711 )
697712
698713 # Pasting, drag & drop, and keyboard layouts
715730 self._flagAnkiText()
716731
717732 def onCut(self):
718 self._markInternal = True
719733 self.triggerPageAction(QWebEnginePage.Cut)
720734
721735 def onCopy(self):
722 self._markInternal = True
723736 self.triggerPageAction(QWebEnginePage.Copy)
724737
725738 def onPaste(self):
818831
819832 # add to media and return resulting html link
820833 return self.editor._addMedia(newpath)
834
835 def flagAnkiText(self):
836 # be ready to adjust when clipboard event fires
837 self._markInternal = True
821838
822839 def _flagAnkiText(self):
823840 # add a comment in the clipboard html so we can tell text is copied
9696 It's a good idea to run Tools>Check Database to ensure your collection \
9797 is not corrupt.
9898 """))
99
99100 stdText = _("""\
100 An error occurred. It may have been caused by a harmless bug, <br>
101 or your deck may have a problem.
102 <p>To confirm it's not a problem with your deck, please run
103 <b>Tools &gt; Check Database</b>.
104 <p>If that doesn't fix the problem, please copy the following<br>
105 into a bug report:""")
101 <h1>Error</h1>
102
103 <p>An error occurred. Please use <b>Tools &gt; Check Database</b> to see if \
104 that fixes the problem.</p>
105
106 <p>If problems persist, please report the problem on our \
107 <a href="https://help.ankiweb.net">support site</a>. Please copy and paste \
108 the information below into your report.</p>""")
109
106110 pluginText = _("""\
111 <h1>Error</h1>
112
107113 <p>An error occurred. Please start Anki while holding down the shift \
108114 key, which will temporarily disable the add-ons you have installed.</p>
109115
110 <p>If the problem occurs even with add-ons disabled, please report the \
111 issue on our support site.</p>
116 <p>If the issue only occurs when add-ons are enabled, please use the \
117 Tools&gt;Add-ons menu item to disable some add-ons and restart Anki, \
118 repeating until you discover the add-on that is causing the problem.</p>
112119
113 <p>If the issue only occurs when add-ons are enabled, plesae use the \
114 Tools&gt;Add-ons menu item to disable one add-on and restart Anki, \
115 repeating until you discover the add-on that is causing the problem.</p>
120 <p>When you've discovered the add-on that is causing the problem, please \
121 report the issue on the <a href="https://help.ankiweb.net/discussions/add-ons/">\
122 add-ons section</a> of our support site.
123
124 <p>Debug info:</p>
116125 """)
117126 if self.mw.addonManager.dirty:
118127 txt = pluginText
119128 else:
120129 txt = stdText
121130 # show dialog
131 error = self._supportText() + "\n" + error
132
122133 txt = txt + "<div style='white-space: pre-wrap'>" + error + "</div>"
123134 showText(txt, type="html")
135
136 def _supportText(self):
137 import platform
138 from aqt import appVersion
139
140 if isWin:
141 platname = "Windows " + platform.win32_ver()[0]
142 elif isMac:
143 platname = "Mac " + platform.mac_ver()[0]
144 else:
145 platname = "Linux"
146
147 return f"""\
148 Anki {appVersion} Python {platform.python_version()} Qt {QT_VERSION_STR} PyQt {PYQT_VERSION_STR}
149 Platform: {platname}
150 Flags: frz={getattr(sys, "frozen", False)} ao={self.mw.addonManager.dirty}
151 """
1010 from anki.exporting import exporters
1111 from anki.hooks import addHook, remHook
1212 from anki.lang import ngettext
13
13 import time
1414
1515 class ExportDialog(QDialog):
1616
2525 self.exec_()
2626
2727 def setup(self, did):
28 self.frm.format.insertItems(0, list(zip(*exporters()))[0])
28 self.exporters = exporters()
29 # if a deck specified, start with .apkg type selected
30 idx = 0
31 if did:
32 for c, (k,e) in enumerate(self.exporters):
33 if e.ext == ".apkg":
34 idx = c
35 break
36 self.frm.format.insertItems(0, [e[0] for e in self.exporters])
37 self.frm.format.setCurrentIndex(idx)
2938 self.frm.format.activated.connect(self.exporterChanged)
30 self.exporterChanged(0)
39 self.exporterChanged(idx)
40 # deck list
3141 self.decks = [_("All Decks")] + sorted(self.col.decks.allNames())
3242 self.frm.deck.addItems(self.decks)
3343 # save button
4050 self.frm.deck.setCurrentIndex(index)
4151
4252 def exporterChanged(self, idx):
43 self.exporter = exporters()[idx][1](self.col)
44 self.isApkg = hasattr(self.exporter, "includeSched")
53 self.exporter = self.exporters[idx][1](self.col)
54 self.isApkg = self.exporter.ext == ".apkg"
55 self.isVerbatim = getattr(self.exporter, "verbatim", False)
4556 self.isTextNote = hasattr(self.exporter, "includeTags")
46 self.hideTags = hasattr(self.exporter, "hideTags")
47 self.frm.includeSched.setVisible(self.isApkg)
48 self.frm.includeMedia.setVisible(self.isApkg)
57 self.frm.includeSched.setVisible(
58 getattr(self.exporter, "includeSched", None) is not None)
59 self.frm.includeMedia.setVisible(
60 getattr(self.exporter, "includeMedia", None) is not None)
4961 self.frm.includeTags.setVisible(
50 not self.isApkg and not self.hideTags)
62 getattr(self.exporter, "includeTags", None) is not None)
63 # show deck list?
64 self.frm.deck.setVisible(not self.isVerbatim)
5165
5266 def accept(self):
5367 self.exporter.includeSched = (
6175 else:
6276 name = self.decks[self.frm.deck.currentIndex()]
6377 self.exporter.did = self.col.decks.id(name)
64 if (self.isApkg and self.exporter.includeSched and not
65 self.exporter.did):
66 verbatim = True
67 # it's a verbatim apkg export, so place on desktop instead of
68 # choosing file; use homedir if no desktop
69 usingHomedir = False
70 file = os.path.join(QStandardPaths.writableLocation(
71 QStandardPaths.DesktopLocation), "collection.apkg")
72 if not os.path.exists(os.path.dirname(file)):
73 usingHomedir = True
74 file = os.path.join(QStandardPaths.writableLocation(
75 QStandardPaths.HomeLocation), "collection.apkg")
76 if os.path.exists(file):
77 if usingHomedir:
78 question = _("%s already exists in your home directory. Overwrite it?")
79 else:
80 question = _("%s already exists on your desktop. Overwrite it?")
81 if not askUser(question % "collection.apkg"):
82 return
78 if self.isVerbatim:
79 name = time.strftime("-%Y-%m-%d@%H-%M-%S",
80 time.localtime(time.time()))
81 deck_name = _("collection")+name
8382 else:
84 verbatim = False
8583 # Get deck name and remove invalid filename characters
8684 deck_name = self.decks[self.frm.deck.currentIndex()]
8785 deck_name = re.sub('[\\\\/?<>:*|"^]', '_', deck_name)
88 filename = '{0}{1}'.format(deck_name, self.exporter.ext)
89 while 1:
90 file = getSaveFile(self, _("Export"), "export",
91 self.exporter.key, self.exporter.ext,
92 fname=filename)
93 if not file:
94 return
95 if checkInvalidFilename(os.path.basename(file), dirsep=False):
96 continue
97 break
86
87 filename = '{0}{1}'.format(deck_name, self.exporter.ext)
88 while 1:
89 file = getSaveFile(self, _("Export"), "export",
90 self.exporter.key, self.exporter.ext,
91 fname=filename)
92 if not file:
93 return
94 if checkInvalidFilename(os.path.basename(file), dirsep=False):
95 continue
96 break
9897 self.hide()
9998 if file:
10099 self.mw.progress.start(immediate=True)
112111 addHook("exportedMediaFiles", exportedMedia)
113112 self.exporter.exportInto(file)
114113 remHook("exportedMediaFiles", exportedMedia)
115 if verbatim:
116 if usingHomedir:
117 msg = _("A file called %s was saved in your home directory.")
118 else:
119 msg = _("A file called %s was saved on your desktop.")
120 msg = msg % "collection.apkg"
121 period = 5000
114 period = 3000
115 if self.isVerbatim:
116 msg = _("Collection exported.")
122117 else:
123 period = 3000
124118 if self.isTextNote:
125119 msg = ngettext("%d note exported.", "%d notes exported.",
126120 self.exporter.count) % self.exporter.count
4545 self.tab = QtWidgets.QWidget()
4646 self.tab.setObjectName("tab")
4747 self.verticalLayout_2 = QtWidgets.QVBoxLayout(self.tab)
48 self.verticalLayout_2.setContentsMargins(0, 0, 0, 0)
48 self.verticalLayout_2.setContentsMargins(12, 12, 12, 12)
4949 self.verticalLayout_2.setObjectName("verticalLayout_2")
5050 self.gridLayout = QtWidgets.QGridLayout()
51 self.gridLayout.setSpacing(12)
5152 self.gridLayout.setObjectName("gridLayout")
5253 self.label_27 = QtWidgets.QLabel(self.tab)
5354 self.label_27.setObjectName("label_27")
118119 self.tab_3 = QtWidgets.QWidget()
119120 self.tab_3.setObjectName("tab_3")
120121 self.verticalLayout_4 = QtWidgets.QVBoxLayout(self.tab_3)
121 self.verticalLayout_4.setContentsMargins(0, 0, 0, 0)
122 self.verticalLayout_4.setContentsMargins(12, 12, 12, 12)
122123 self.verticalLayout_4.setObjectName("verticalLayout_4")
123124 self.gridLayout_3 = QtWidgets.QGridLayout()
125 self.gridLayout_3.setSpacing(12)
124126 self.gridLayout_3.setObjectName("gridLayout_3")
125127 self.label_20 = QtWidgets.QLabel(self.tab_3)
126128 self.label_20.setObjectName("label_20")
181183 self.tab_2 = QtWidgets.QWidget()
182184 self.tab_2.setObjectName("tab_2")
183185 self.verticalLayout_3 = QtWidgets.QVBoxLayout(self.tab_2)
184 self.verticalLayout_3.setContentsMargins(0, 0, 0, 0)
186 self.verticalLayout_3.setContentsMargins(12, 12, 12, 12)
185187 self.verticalLayout_3.setObjectName("verticalLayout_3")
186188 self.gridLayout_2 = QtWidgets.QGridLayout()
189 self.gridLayout_2.setSpacing(12)
187190 self.gridLayout_2.setObjectName("gridLayout_2")
188191 self.label_17 = QtWidgets.QLabel(self.tab_2)
189192 self.label_17.setObjectName("label_17")
247250 self.tab_5 = QtWidgets.QWidget()
248251 self.tab_5.setObjectName("tab_5")
249252 self.verticalLayout_6 = QtWidgets.QVBoxLayout(self.tab_5)
250 self.verticalLayout_6.setContentsMargins(0, 0, 0, 0)
253 self.verticalLayout_6.setContentsMargins(12, 12, 12, 12)
251254 self.verticalLayout_6.setObjectName("verticalLayout_6")
252255 self.gridLayout_5 = QtWidgets.QGridLayout()
256 self.gridLayout_5.setSpacing(12)
253257 self.gridLayout_5.setObjectName("gridLayout_5")
254258 self.label_25 = QtWidgets.QLabel(self.tab_5)
255259 self.label_25.setObjectName("label_25")
280284 self.tab_4 = QtWidgets.QWidget()
281285 self.tab_4.setObjectName("tab_4")
282286 self.verticalLayout_5 = QtWidgets.QVBoxLayout(self.tab_4)
283 self.verticalLayout_5.setContentsMargins(0, 0, 0, 0)
287 self.verticalLayout_5.setContentsMargins(12, 12, 12, 12)
288 self.verticalLayout_5.setSpacing(12)
284289 self.verticalLayout_5.setObjectName("verticalLayout_5")
285290 self.label_22 = QtWidgets.QLabel(self.tab_4)
286291 self.label_22.setObjectName("label_22")
88 from PyQt5 import QtCore
99
1010 qt_resource_data = b"\
11 \x00\x00\x02\xd7\
12 \x89\
13 \x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52\x00\
14 \x00\x00\x3c\x00\x00\x00\x3c\x08\x06\x00\x00\x00\x3a\xfc\xd9\x72\
15 \x00\x00\x00\x19\x74\x45\x58\x74\x53\x6f\x66\x74\x77\x61\x72\x65\
16 \x00\x41\x64\x6f\x62\x65\x20\x49\x6d\x61\x67\x65\x52\x65\x61\x64\
17 \x79\x71\xc9\x65\x3c\x00\x00\x02\x79\x49\x44\x41\x54\x78\xda\xec\
18 \x9a\xd1\x6d\xc2\x30\x10\x40\x0d\x62\x80\x6c\x40\x3a\x01\xde\xa0\
19 \x19\x21\x23\xa4\x13\xc0\x06\xf5\x06\xa8\x13\xa4\x1b\xa4\x1b\x44\
20 \x4c\x10\x98\x20\x6c\x10\x36\x68\x8d\xe4\x48\xc8\x75\x82\xef\xb0\
21 \x0f\x87\xf8\xa4\xfb\x31\x89\xc3\xf3\x9d\xed\xbb\xb3\x17\x8c\x56\
22 \xb8\xd4\x5c\xea\x46\x6a\xa2\xda\x2e\x52\x4f\x52\x7f\xa4\x1e\xd9\
23 \x8b\xc8\x15\xb2\x91\xfa\x7b\x47\x5b\xf5\xec\x64\xe5\x6a\xc5\xca\
24 \x02\x54\xd7\xea\xc6\x03\x26\x05\xdb\x20\x60\x7b\x6d\xd4\x14\x98\
25 \x05\x6c\xaf\xdd\x54\x2c\xed\x02\xf6\xd6\xd2\x41\x8b\x70\x08\xdb\
26 \xab\x08\x15\x36\xf5\x00\xdb\x6b\x1a\x22\x70\xe9\x11\xb8\x9e\x93\
27 \x75\x9d\x59\x79\xe9\x10\x78\x4b\x30\xa8\xdb\x90\x2c\xdc\x12\x58\
28 \xb8\x0b\x05\x96\x13\xc0\xf6\xca\x43\x70\xe9\x8c\x38\x2e\x47\xcb\
29 \xea\x0e\x04\x57\x19\xcc\x59\xe9\x90\x6c\x08\x81\x37\x3e\x81\x3f\
30 \xb5\xb6\x2b\xfc\x97\xd4\x6f\xc3\x0a\x4d\xb9\x1b\xe8\x61\x6c\xae\
31 \xda\xfb\xb4\x93\xab\xb4\xf3\x0d\x02\x3c\x34\x57\xd3\x27\xbb\x34\
32 \x1f\xd8\xff\x4d\xf1\x3c\x68\x0e\xbf\x0f\xb4\x87\x96\xa4\x5f\x7c\
33 \x2f\x5a\x17\x36\x61\x59\xb2\x99\xc9\x12\x61\xc9\xf4\x55\x81\x4f\
34 \x13\x01\x4e\x7c\xbb\xf4\xfa\xc9\x0b\xd9\xd1\x62\xd5\x1e\xfc\x4f\
35 \x4b\x40\xc7\x63\x1f\x38\x3f\x71\x55\xe6\x90\x29\x89\x05\x4e\x2c\
36 \xdd\xdf\x87\x1c\x2c\x23\xaf\x03\xa6\xf3\x6e\x20\x80\xcf\x0d\x81\
37 \x07\x55\xf2\x90\x21\xff\xa3\x95\x0c\xd5\x94\x4b\xc0\xe0\xf8\x4c\
38 \x0f\xf3\x91\x67\x51\xd5\xce\x02\xd0\x61\x49\x00\x5c\x5a\x7e\x13\
39 \x5d\xe9\x4c\x98\x7d\x25\x91\x13\xbb\xf3\x58\x49\x69\xf7\xc8\x22\
40 \x51\x31\xfb\x22\x79\x4d\x58\xc4\x2b\x7d\xd5\xbe\x32\x80\x95\xb3\
41 \x00\xac\x5b\xfa\xae\x57\x71\x82\xb9\x5c\x01\x3c\xc9\x49\xaa\x3a\
42 \xb6\x1a\x36\x9a\x6b\x27\x8e\x0b\x7a\xad\xd6\xbf\x60\x44\xb5\xeb\
43 \x1a\xe0\x46\xdc\xd1\x36\xd5\x69\x1e\x94\xfb\x2c\xf0\x41\x2b\x93\
44 \xae\xa1\x75\xd8\x7b\xfd\xed\x7d\x84\x75\x02\x01\x8d\x39\x49\x6c\
45 \x80\xb0\x2d\xf3\x78\xac\x5a\x33\xd8\xe9\x7d\xa2\x06\xaa\xb3\xb4\
46 \xaa\xd0\xde\x2f\x98\xe7\x5a\xb5\x4d\x30\xd2\x58\x2c\x34\x99\xe1\
47 \xbd\x42\x0d\x48\xab\x3d\x5b\xa9\xdf\xf4\x81\xb2\xb9\x2e\x51\x50\
48 \x55\x0e\x3b\xcb\xed\x04\x13\x04\xec\x2c\xfb\x17\x94\x95\x06\xc8\
49 \xa2\x54\x1b\x2c\x68\xea\x6f\x0f\xe8\x13\x15\x60\x2c\x1e\x84\x4e\
50 \x95\x15\x21\x73\xe8\x6c\x28\x18\x40\x83\x85\x0f\xf6\xff\x30\x80\
51 \xb4\xa6\x84\xb9\x9a\x84\xdd\xaa\x82\xb9\xc7\xb5\x63\x7e\x73\xe2\
52 \x9a\x05\x78\xa3\x27\xf1\x10\x4b\x4f\xe2\x76\x5e\xaa\xc0\x1f\xb1\
53 \x78\x43\xb5\xe5\xb8\xb6\x78\xa1\xe0\x5b\x4b\x6b\x0a\x36\xa1\x1b\
54 \x78\xd8\xbc\x9a\x6c\x5f\x8d\x67\x4b\x11\x38\x02\x47\xe0\x08\x1c\
55 \x81\x03\x92\x15\xf2\x3d\x8e\x88\x6b\x6d\x82\x89\x35\x22\x73\x32\
56 \x65\x5f\xe4\x65\x1e\x4a\x15\xd1\xa5\x23\x70\x04\x9e\x8f\xfc\x09\
57 \x30\x00\xa0\x1c\x74\x67\x26\xea\x15\x76\x00\x00\x00\x00\x49\x45\
58 \x4e\x44\xae\x42\x60\x82\
11 \x00\x00\x02\x68\
12 \x00\
13 \x00\x10\x25\x78\x9c\xed\x97\x5b\x6f\xd3\x30\x14\xc7\xdf\xf7\x29\
14 \x8c\x25\x24\x90\x52\x5f\x13\x3b\xce\x9a\x4d\xda\x85\x09\x69\xc0\
15 \x24\x36\x10\xbc\x85\xc4\x6b\xcd\xd2\x24\x4a\xb2\xb6\xfb\xf6\x9c\
16 \x64\xed\xb4\x6e\xd5\x40\x88\xf1\xb2\xb4\x55\xeb\x73\x72\x7c\x2e\
17 \x3f\xfb\xff\xd0\xf1\xfe\x72\x96\xa3\xb9\xad\x1b\x57\x16\x31\xe6\
18 \x84\x61\x64\x8b\xb4\xcc\x5c\x31\x89\xf1\xc5\xf9\xbb\x51\x88\x51\
19 \xd3\x26\x45\x96\xe4\x65\x61\x63\x5c\x94\x78\x7f\x6f\x67\xfc\xea\
20 \xe8\xd3\xe1\xf9\xb7\xb3\x63\xd4\xcc\x27\xe8\xec\xe2\xe0\xf4\xfd\
21 \x21\xc2\x23\x4a\xbf\xca\x43\x4a\x8f\xce\x8f\xd0\xe7\x2f\x27\x88\
22 \x13\x4e\xe9\xf1\x47\x8c\xf0\xb4\x6d\xab\x88\xd2\xc5\x62\x41\x16\
23 \x92\x94\xf5\x84\x9e\xd4\x49\x35\x75\x69\x43\x21\x90\x76\x81\xb0\
24 \x89\x42\x32\xce\x49\xd6\x66\x18\x4a\x74\x99\x17\x2e\x6b\xa7\xd0\
25 \x16\x63\xaf\x31\x9a\x5a\x37\x99\xb6\x6b\x6b\xee\xec\xe2\xa0\x5c\
26 \xc6\x98\x21\x86\x54\xf7\xc1\xf7\xe7\xe0\x18\xc1\x64\x45\x13\x6f\
27 \xa9\x2d\x18\x63\x5d\xad\x55\x48\xb4\xcc\x5d\x71\xb5\x2d\x90\x1b\
28 \x63\x68\xff\xb4\x0f\x8d\x9a\x2a\x49\x81\x41\x55\xdb\xc6\xd6\x73\
29 \xdb\x91\xb9\xc9\xc1\x71\xe9\xf2\x7c\x54\x5f\xe7\x36\xb2\x73\x5b\
30 \x94\x59\xb6\x9b\xe6\xae\xda\xf4\x34\x6d\x5d\x5e\xd9\x11\x24\xb3\
31 \x69\x52\x45\x75\x79\x5d\x6c\x38\x7f\x96\xae\xd8\xf4\xce\x5c\x6b\
32 \xeb\xdc\xc1\x4f\xc4\x49\xb0\x0b\x4c\x10\xbc\xc6\x13\xd4\xd6\x49\
33 \xd1\x5c\x96\xf5\x2c\xc6\xb3\xa4\xad\xdd\xf2\x0d\xf7\x18\xbc\xb9\
34 \x37\xf2\xb9\xf6\xd8\xdb\x55\xe8\x2a\xdc\x65\x31\x4e\xcb\x3c\xb7\
35 \x69\x0b\x70\xf0\x53\xdb\xb9\xd0\xfe\xe6\xfe\x3e\x47\x0d\x5b\x11\
36 \xa0\xe6\xca\x00\xd7\x1b\x60\x8e\xd7\x47\xd3\x61\x5f\x1f\x4c\xb7\
37 \xbe\x47\x24\x2a\xe0\xc6\xec\x62\xfa\x20\xd9\xb6\xfe\x19\x31\x7e\
38 \xe0\x4b\xd9\xf7\x71\x67\x18\x41\x94\x62\xdc\x13\x84\x2b\xa5\xcc\
39 \xc3\xb6\x9e\xc8\xa6\x82\x80\x73\xc8\x46\x18\xe3\x4a\xfa\x42\x78\
40 \xa3\x7e\x6d\x84\x66\x06\xdc\x5a\x8b\x00\x48\x71\x61\x0c\x31\xd2\
41 \x13\x21\x91\xa1\x0e\xb7\x15\xe8\x8b\x54\x49\x3b\x45\x40\xf1\x83\
42 \xd2\xd2\xd3\xa7\x2a\x10\xab\x6f\x0e\xbd\xf2\x53\xa5\x42\x22\xa0\
43 \x4f\xb8\xbb\x8c\xcb\x5b\xd3\x97\x9e\x14\xc4\x30\x30\xf5\xc6\x52\
44 \x7f\xdf\x02\xe9\xf6\xc4\xa3\x1f\x79\x92\x5e\xad\x8f\xbf\x07\x1c\
45 \xf9\xd5\xf2\x11\xc2\xbe\x27\x3a\xf9\xe7\x34\x24\x93\xc0\xc1\x13\
46 \x01\x11\x81\x0a\x5e\x3a\x0d\x61\x14\xd1\xa1\x27\x41\x7b\xdc\xff\
47 \x0b\x1a\xff\x77\xe0\xc7\x8e\x3f\xd7\x99\x52\x83\xce\x06\x9d\x0d\
48 \x3a\x7b\x6e\x9d\xc1\xa0\x4c\x19\xe5\x8d\x84\x26\x42\x85\x6a\x10\
49 \xda\x33\x5f\xad\x41\x68\x2f\x53\x68\x1a\xe6\xf7\x75\x30\x08\x6d\
50 \x10\xda\x20\xb4\xdf\x0d\xbc\xe1\xb8\x33\xfa\xc5\xb8\xfb\x97\xbc\
51 \xb7\xf3\x0b\x46\x33\xee\x37\
5952 \x00\x00\x04\x30\
6053 \x3c\
6154 \x3f\x78\x6d\x6c\x20\x76\x65\x72\x73\x69\x6f\x6e\x3d\x22\x31\x2e\
125118 \x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x3c\x2f\x67\
126119 \x3e\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x3c\x2f\x67\x3e\x0a\x20\
127120 \x20\x20\x20\x3c\x2f\x67\x3e\x0a\x3c\x2f\x73\x76\x67\x3e\x0a\
128 \x00\x00\x02\x68\
129 \x00\
130 \x00\x10\x25\x78\x9c\xed\x97\x5b\x6f\xd3\x30\x14\xc7\xdf\xf7\x29\
131 \x8c\x25\x24\x90\x52\x5f\x13\x3b\xce\x9a\x4d\xda\x85\x09\x69\xc0\
132 \x24\x36\x10\xbc\x85\xc4\x6b\xcd\xd2\x24\x4a\xb2\xb6\xfb\xf6\x9c\
133 \x64\xed\xb4\x6e\xd5\x40\x88\xf1\xb2\xb4\x55\xeb\x73\x72\x7c\x2e\
134 \x3f\xfb\xff\xd0\xf1\xfe\x72\x96\xa3\xb9\xad\x1b\x57\x16\x31\xe6\
135 \x84\x61\x64\x8b\xb4\xcc\x5c\x31\x89\xf1\xc5\xf9\xbb\x51\x88\x51\
136 \xd3\x26\x45\x96\xe4\x65\x61\x63\x5c\x94\x78\x7f\x6f\x67\xfc\xea\
137 \xe8\xd3\xe1\xf9\xb7\xb3\x63\xd4\xcc\x27\xe8\xec\xe2\xe0\xf4\xfd\
138 \x21\xc2\x23\x4a\xbf\xca\x43\x4a\x8f\xce\x8f\xd0\xe7\x2f\x27\x88\
139 \x13\x4e\xe9\xf1\x47\x8c\xf0\xb4\x6d\xab\x88\xd2\xc5\x62\x41\x16\
140 \x92\x94\xf5\x84\x9e\xd4\x49\x35\x75\x69\x43\x21\x90\x76\x81\xb0\
141 \x89\x42\x32\xce\x49\xd6\x66\x18\x4a\x74\x99\x17\x2e\x6b\xa7\xd0\
142 \x16\x63\xaf\x31\x9a\x5a\x37\x99\xb6\x6b\x6b\xee\xec\xe2\xa0\x5c\
143 \xc6\x98\x21\x86\x54\xf7\xc1\xf7\xe7\xe0\x18\xc1\x64\x45\x13\x6f\
144 \xa9\x2d\x18\x63\x5d\xad\x55\x48\xb4\xcc\x5d\x71\xb5\x2d\x90\x1b\
145 \x63\x68\xff\xb4\x0f\x8d\x9a\x2a\x49\x81\x41\x55\xdb\xc6\xd6\x73\
146 \xdb\x91\xb9\xc9\xc1\x71\xe9\xf2\x7c\x54\x5f\xe7\x36\xb2\x73\x5b\
147 \x94\x59\xb6\x9b\xe6\xae\xda\xf4\x34\x6d\x5d\x5e\xd9\x11\x24\xb3\
148 \x69\x52\x45\x75\x79\x5d\x6c\x38\x7f\x96\xae\xd8\xf4\xce\x5c\x6b\
149 \xeb\xdc\xc1\x4f\xc4\x49\xb0\x0b\x4c\x10\xbc\xc6\x13\xd4\xd6\x49\
150 \xd1\x5c\x96\xf5\x2c\xc6\xb3\xa4\xad\xdd\xf2\x0d\xf7\x18\xbc\xb9\
151 \x37\xf2\xb9\xf6\xd8\xdb\x55\xe8\x2a\xdc\x65\x31\x4e\xcb\x3c\xb7\
152 \x69\x0b\x70\xf0\x53\xdb\xb9\xd0\xfe\xe6\xfe\x3e\x47\x0d\x5b\x11\
153 \xa0\xe6\xca\x00\xd7\x1b\x60\x8e\xd7\x47\xd3\x61\x5f\x1f\x4c\xb7\
154 \xbe\x47\x24\x2a\xe0\xc6\xec\x62\xfa\x20\xd9\xb6\xfe\x19\x31\x7e\
155 \xe0\x4b\xd9\xf7\x71\x67\x18\x41\x94\x62\xdc\x13\x84\x2b\xa5\xcc\
156 \xc3\xb6\x9e\xc8\xa6\x82\x80\x73\xc8\x46\x18\xe3\x4a\xfa\x42\x78\
157 \xa3\x7e\x6d\x84\x66\x06\xdc\x5a\x8b\x00\x48\x71\x61\x0c\x31\xd2\
158 \x13\x21\x91\xa1\x0e\xb7\x15\xe8\x8b\x54\x49\x3b\x45\x40\xf1\x83\
159 \xd2\xd2\xd3\xa7\x2a\x10\xab\x6f\x0e\xbd\xf2\x53\xa5\x42\x22\xa0\
160 \x4f\xb8\xbb\x8c\xcb\x5b\xd3\x97\x9e\x14\xc4\x30\x30\xf5\xc6\x52\
161 \x7f\xdf\x02\xe9\xf6\xc4\xa3\x1f\x79\x92\x5e\xad\x8f\xbf\x07\x1c\
162 \xf9\xd5\xf2\x11\xc2\xbe\x27\x3a\xf9\xe7\x34\x24\x93\xc0\xc1\x13\
163 \x01\x11\x81\x0a\x5e\x3a\x0d\x61\x14\xd1\xa1\x27\x41\x7b\xdc\xff\
164 \x0b\x1a\xff\x77\xe0\xc7\x8e\x3f\xd7\x99\x52\x83\xce\x06\x9d\x0d\
165 \x3a\x7b\x6e\x9d\xc1\xa0\x4c\x19\xe5\x8d\x84\x26\x42\x85\x6a\x10\
166 \xda\x33\x5f\xad\x41\x68\x2f\x53\x68\x1a\xe6\xf7\x75\x30\x08\x6d\
167 \x10\xda\x20\xb4\xdf\x0d\xbc\xe1\xb8\x33\xfa\xc5\xb8\xfb\x97\xbc\
168 \xb7\xf3\x0b\x46\x33\xee\x37\
169121 \x00\x00\x06\xb8\
170122 \x89\
171123 \x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52\x00\
365317 \x20\x20\x20\x20\x20\x20\x3c\x2f\x67\x3e\x0a\x20\x20\x20\x20\x20\
366318 \x20\x20\x20\x3c\x2f\x67\x3e\x0a\x20\x20\x20\x20\x3c\x2f\x67\x3e\
367319 \x0a\x3c\x2f\x73\x76\x67\x3e\x0a\
320 \x00\x00\x04\x06\
321 \x3c\
322 \x3f\x78\x6d\x6c\x20\x76\x65\x72\x73\x69\x6f\x6e\x3d\x22\x31\x2e\
323 \x30\x22\x20\x65\x6e\x63\x6f\x64\x69\x6e\x67\x3d\x22\x55\x54\x46\
324 \x2d\x38\x22\x20\x73\x74\x61\x6e\x64\x61\x6c\x6f\x6e\x65\x3d\x22\
325 \x6e\x6f\x22\x3f\x3e\x0a\x3c\x21\x44\x4f\x43\x54\x59\x50\x45\x20\
326 \x73\x76\x67\x20\x50\x55\x42\x4c\x49\x43\x20\x22\x2d\x2f\x2f\x57\
327 \x33\x43\x2f\x2f\x44\x54\x44\x20\x53\x56\x47\x20\x31\x2e\x31\x2f\
328 \x2f\x45\x4e\x22\x20\x22\x68\x74\x74\x70\x3a\x2f\x2f\x77\x77\x77\
329 \x2e\x77\x33\x2e\x6f\x72\x67\x2f\x47\x72\x61\x70\x68\x69\x63\x73\
330 \x2f\x53\x56\x47\x2f\x31\x2e\x31\x2f\x44\x54\x44\x2f\x73\x76\x67\
331 \x31\x31\x2e\x64\x74\x64\x22\x3e\x0a\x3c\x73\x76\x67\x20\x77\x69\
332 \x64\x74\x68\x3d\x22\x31\x30\x30\x25\x22\x20\x68\x65\x69\x67\x68\
333 \x74\x3d\x22\x31\x30\x30\x25\x22\x20\x76\x69\x65\x77\x42\x6f\x78\
334 \x3d\x22\x30\x20\x30\x20\x36\x30\x20\x36\x30\x22\x20\x76\x65\x72\
335 \x73\x69\x6f\x6e\x3d\x22\x31\x2e\x31\x22\x20\x78\x6d\x6c\x6e\x73\
336 \x3d\x22\x68\x74\x74\x70\x3a\x2f\x2f\x77\x77\x77\x2e\x77\x33\x2e\
337 \x6f\x72\x67\x2f\x32\x30\x30\x30\x2f\x73\x76\x67\x22\x20\x78\x6d\
338 \x6c\x6e\x73\x3a\x78\x6c\x69\x6e\x6b\x3d\x22\x68\x74\x74\x70\x3a\
339 \x2f\x2f\x77\x77\x77\x2e\x77\x33\x2e\x6f\x72\x67\x2f\x31\x39\x39\
340 \x39\x2f\x78\x6c\x69\x6e\x6b\x22\x20\x78\x6d\x6c\x3a\x73\x70\x61\
341 \x63\x65\x3d\x22\x70\x72\x65\x73\x65\x72\x76\x65\x22\x20\x73\x74\
342 \x79\x6c\x65\x3d\x22\x66\x69\x6c\x6c\x2d\x72\x75\x6c\x65\x3a\x65\
343 \x76\x65\x6e\x6f\x64\x64\x3b\x63\x6c\x69\x70\x2d\x72\x75\x6c\x65\
344 \x3a\x65\x76\x65\x6e\x6f\x64\x64\x3b\x73\x74\x72\x6f\x6b\x65\x2d\
345 \x6c\x69\x6e\x65\x63\x61\x70\x3a\x72\x6f\x75\x6e\x64\x3b\x73\x74\
346 \x72\x6f\x6b\x65\x2d\x6c\x69\x6e\x65\x6a\x6f\x69\x6e\x3a\x72\x6f\
347 \x75\x6e\x64\x3b\x73\x74\x72\x6f\x6b\x65\x2d\x6d\x69\x74\x65\x72\
348 \x6c\x69\x6d\x69\x74\x3a\x31\x2e\x35\x3b\x22\x3e\x0a\x20\x20\x20\
349 \x20\x3c\x67\x20\x74\x72\x61\x6e\x73\x66\x6f\x72\x6d\x3d\x22\x6d\
350 \x61\x74\x72\x69\x78\x28\x31\x2c\x30\x2c\x30\x2c\x31\x2c\x2d\x32\
351 \x38\x30\x2c\x30\x29\x22\x3e\x0a\x20\x20\x20\x20\x20\x20\x20\x20\
352 \x3c\x67\x20\x69\x64\x3d\x22\x68\x65\x61\x72\x74\x22\x20\x74\x72\
353 \x61\x6e\x73\x66\x6f\x72\x6d\x3d\x22\x6d\x61\x74\x72\x69\x78\x28\
354 \x31\x2c\x30\x2c\x30\x2c\x31\x2c\x2d\x31\x34\x31\x31\x2c\x30\x29\
355 \x22\x3e\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x3c\
356 \x72\x65\x63\x74\x20\x78\x3d\x22\x31\x36\x39\x31\x22\x20\x79\x3d\
357 \x22\x30\x22\x20\x77\x69\x64\x74\x68\x3d\x22\x36\x30\x22\x20\x68\
358 \x65\x69\x67\x68\x74\x3d\x22\x36\x30\x22\x20\x73\x74\x79\x6c\x65\
359 \x3d\x22\x66\x69\x6c\x6c\x3a\x6e\x6f\x6e\x65\x3b\x22\x2f\x3e\x0a\
360 \x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x3c\x67\x20\x74\
361 \x72\x61\x6e\x73\x66\x6f\x72\x6d\x3d\x22\x6d\x61\x74\x72\x69\x78\
362 \x28\x30\x2e\x39\x36\x30\x32\x34\x31\x2c\x30\x2c\x30\x2c\x30\x2e\
363 \x39\x36\x30\x32\x34\x31\x2c\x31\x34\x33\x31\x2e\x30\x31\x2c\x33\
364 \x2e\x31\x34\x37\x30\x31\x29\x22\x3e\x0a\x20\x20\x20\x20\x20\x20\
365 \x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x3c\x70\x61\x74\x68\x20\
366 \x64\x3d\x22\x4d\x33\x30\x32\x2c\x31\x30\x2e\x38\x43\x33\x30\x37\
367 \x2e\x36\x38\x34\x2c\x30\x20\x33\x31\x39\x2e\x30\x35\x33\x2c\x30\
368 \x20\x33\x32\x34\x2e\x37\x33\x37\x2c\x35\x2e\x34\x43\x33\x33\x30\
369 \x2e\x34\x32\x31\x2c\x31\x30\x2e\x38\x20\x33\x33\x30\x2e\x34\x32\
370 \x31\x2c\x32\x31\x2e\x36\x20\x33\x32\x34\x2e\x37\x33\x37\x2c\x33\
371 \x32\x2e\x34\x43\x33\x32\x30\x2e\x37\x35\x38\x2c\x34\x30\x2e\x35\
372 \x20\x33\x31\x30\x2e\x35\x32\x36\x2c\x34\x38\x2e\x36\x20\x33\x30\
373 \x32\x2c\x35\x34\x43\x32\x39\x33\x2e\x34\x37\x34\x2c\x34\x38\x2e\
374 \x36\x20\x32\x38\x33\x2e\x32\x34\x32\x2c\x34\x30\x2e\x35\x20\x32\
375 \x37\x39\x2e\x32\x36\x33\x2c\x33\x32\x2e\x34\x43\x32\x37\x33\x2e\
376 \x35\x37\x39\x2c\x32\x31\x2e\x36\x20\x32\x37\x33\x2e\x35\x37\x39\
377 \x2c\x31\x30\x2e\x38\x20\x32\x37\x39\x2e\x32\x36\x33\x2c\x35\x2e\
378 \x34\x43\x32\x38\x34\x2e\x39\x34\x37\x2c\x30\x20\x32\x39\x36\x2e\
379 \x33\x31\x36\x2c\x30\x20\x33\x30\x32\x2c\x31\x30\x2e\x38\x5a\x22\
380 \x20\x73\x74\x79\x6c\x65\x3d\x22\x66\x69\x6c\x6c\x3a\x6e\x6f\x6e\
381 \x65\x3b\x73\x74\x72\x6f\x6b\x65\x3a\x62\x6c\x61\x63\x6b\x3b\x73\
382 \x74\x72\x6f\x6b\x65\x2d\x77\x69\x64\x74\x68\x3a\x32\x2e\x38\x32\
383 \x70\x78\x3b\x22\x2f\x3e\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\
384 \x20\x20\x20\x3c\x2f\x67\x3e\x0a\x20\x20\x20\x20\x20\x20\x20\x20\
385 \x3c\x2f\x67\x3e\x0a\x20\x20\x20\x20\x3c\x2f\x67\x3e\x0a\x3c\x2f\
386 \x73\x76\x67\x3e\x0a\
368387 \x00\x00\x05\x55\
369388 \x3c\
370389 \x3f\x78\x6d\x6c\x20\x76\x65\x72\x73\x69\x6f\x6e\x3d\x22\x31\x2e\
453472 \x20\x20\x3c\x2f\x67\x3e\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x3c\
454473 \x2f\x67\x3e\x0a\x20\x20\x20\x20\x3c\x2f\x67\x3e\x0a\x3c\x2f\x73\
455474 \x76\x67\x3e\x0a\
456 \x00\x00\x04\x06\
457 \x3c\
458 \x3f\x78\x6d\x6c\x20\x76\x65\x72\x73\x69\x6f\x6e\x3d\x22\x31\x2e\
459 \x30\x22\x20\x65\x6e\x63\x6f\x64\x69\x6e\x67\x3d\x22\x55\x54\x46\
460 \x2d\x38\x22\x20\x73\x74\x61\x6e\x64\x61\x6c\x6f\x6e\x65\x3d\x22\
461 \x6e\x6f\x22\x3f\x3e\x0a\x3c\x21\x44\x4f\x43\x54\x59\x50\x45\x20\
462 \x73\x76\x67\x20\x50\x55\x42\x4c\x49\x43\x20\x22\x2d\x2f\x2f\x57\
463 \x33\x43\x2f\x2f\x44\x54\x44\x20\x53\x56\x47\x20\x31\x2e\x31\x2f\
464 \x2f\x45\x4e\x22\x20\x22\x68\x74\x74\x70\x3a\x2f\x2f\x77\x77\x77\
465 \x2e\x77\x33\x2e\x6f\x72\x67\x2f\x47\x72\x61\x70\x68\x69\x63\x73\
466 \x2f\x53\x56\x47\x2f\x31\x2e\x31\x2f\x44\x54\x44\x2f\x73\x76\x67\
467 \x31\x31\x2e\x64\x74\x64\x22\x3e\x0a\x3c\x73\x76\x67\x20\x77\x69\
468 \x64\x74\x68\x3d\x22\x31\x30\x30\x25\x22\x20\x68\x65\x69\x67\x68\
469 \x74\x3d\x22\x31\x30\x30\x25\x22\x20\x76\x69\x65\x77\x42\x6f\x78\
470 \x3d\x22\x30\x20\x30\x20\x36\x30\x20\x36\x30\x22\x20\x76\x65\x72\
471 \x73\x69\x6f\x6e\x3d\x22\x31\x2e\x31\x22\x20\x78\x6d\x6c\x6e\x73\
472 \x3d\x22\x68\x74\x74\x70\x3a\x2f\x2f\x77\x77\x77\x2e\x77\x33\x2e\
473 \x6f\x72\x67\x2f\x32\x30\x30\x30\x2f\x73\x76\x67\x22\x20\x78\x6d\
474 \x6c\x6e\x73\x3a\x78\x6c\x69\x6e\x6b\x3d\x22\x68\x74\x74\x70\x3a\
475 \x2f\x2f\x77\x77\x77\x2e\x77\x33\x2e\x6f\x72\x67\x2f\x31\x39\x39\
476 \x39\x2f\x78\x6c\x69\x6e\x6b\x22\x20\x78\x6d\x6c\x3a\x73\x70\x61\
477 \x63\x65\x3d\x22\x70\x72\x65\x73\x65\x72\x76\x65\x22\x20\x73\x74\
478 \x79\x6c\x65\x3d\x22\x66\x69\x6c\x6c\x2d\x72\x75\x6c\x65\x3a\x65\
479 \x76\x65\x6e\x6f\x64\x64\x3b\x63\x6c\x69\x70\x2d\x72\x75\x6c\x65\
480 \x3a\x65\x76\x65\x6e\x6f\x64\x64\x3b\x73\x74\x72\x6f\x6b\x65\x2d\
481 \x6c\x69\x6e\x65\x63\x61\x70\x3a\x72\x6f\x75\x6e\x64\x3b\x73\x74\
482 \x72\x6f\x6b\x65\x2d\x6c\x69\x6e\x65\x6a\x6f\x69\x6e\x3a\x72\x6f\
483 \x75\x6e\x64\x3b\x73\x74\x72\x6f\x6b\x65\x2d\x6d\x69\x74\x65\x72\
484 \x6c\x69\x6d\x69\x74\x3a\x31\x2e\x35\x3b\x22\x3e\x0a\x20\x20\x20\
485 \x20\x3c\x67\x20\x74\x72\x61\x6e\x73\x66\x6f\x72\x6d\x3d\x22\x6d\
486 \x61\x74\x72\x69\x78\x28\x31\x2c\x30\x2c\x30\x2c\x31\x2c\x2d\x32\
487 \x38\x30\x2c\x30\x29\x22\x3e\x0a\x20\x20\x20\x20\x20\x20\x20\x20\
488 \x3c\x67\x20\x69\x64\x3d\x22\x68\x65\x61\x72\x74\x22\x20\x74\x72\
489 \x61\x6e\x73\x66\x6f\x72\x6d\x3d\x22\x6d\x61\x74\x72\x69\x78\x28\
490 \x31\x2c\x30\x2c\x30\x2c\x31\x2c\x2d\x31\x34\x31\x31\x2c\x30\x29\
491 \x22\x3e\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x3c\
492 \x72\x65\x63\x74\x20\x78\x3d\x22\x31\x36\x39\x31\x22\x20\x79\x3d\
493 \x22\x30\x22\x20\x77\x69\x64\x74\x68\x3d\x22\x36\x30\x22\x20\x68\
494 \x65\x69\x67\x68\x74\x3d\x22\x36\x30\x22\x20\x73\x74\x79\x6c\x65\
495 \x3d\x22\x66\x69\x6c\x6c\x3a\x6e\x6f\x6e\x65\x3b\x22\x2f\x3e\x0a\
496 \x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x3c\x67\x20\x74\
497 \x72\x61\x6e\x73\x66\x6f\x72\x6d\x3d\x22\x6d\x61\x74\x72\x69\x78\
498 \x28\x30\x2e\x39\x36\x30\x32\x34\x31\x2c\x30\x2c\x30\x2c\x30\x2e\
499 \x39\x36\x30\x32\x34\x31\x2c\x31\x34\x33\x31\x2e\x30\x31\x2c\x33\
500 \x2e\x31\x34\x37\x30\x31\x29\x22\x3e\x0a\x20\x20\x20\x20\x20\x20\
501 \x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x3c\x70\x61\x74\x68\x20\
502 \x64\x3d\x22\x4d\x33\x30\x32\x2c\x31\x30\x2e\x38\x43\x33\x30\x37\
503 \x2e\x36\x38\x34\x2c\x30\x20\x33\x31\x39\x2e\x30\x35\x33\x2c\x30\
504 \x20\x33\x32\x34\x2e\x37\x33\x37\x2c\x35\x2e\x34\x43\x33\x33\x30\
505 \x2e\x34\x32\x31\x2c\x31\x30\x2e\x38\x20\x33\x33\x30\x2e\x34\x32\
506 \x31\x2c\x32\x31\x2e\x36\x20\x33\x32\x34\x2e\x37\x33\x37\x2c\x33\
507 \x32\x2e\x34\x43\x33\x32\x30\x2e\x37\x35\x38\x2c\x34\x30\x2e\x35\
508 \x20\x33\x31\x30\x2e\x35\x32\x36\x2c\x34\x38\x2e\x36\x20\x33\x30\
509 \x32\x2c\x35\x34\x43\x32\x39\x33\x2e\x34\x37\x34\x2c\x34\x38\x2e\
510 \x36\x20\x32\x38\x33\x2e\x32\x34\x32\x2c\x34\x30\x2e\x35\x20\x32\
511 \x37\x39\x2e\x32\x36\x33\x2c\x33\x32\x2e\x34\x43\x32\x37\x33\x2e\
512 \x35\x37\x39\x2c\x32\x31\x2e\x36\x20\x32\x37\x33\x2e\x35\x37\x39\
513 \x2c\x31\x30\x2e\x38\x20\x32\x37\x39\x2e\x32\x36\x33\x2c\x35\x2e\
514 \x34\x43\x32\x38\x34\x2e\x39\x34\x37\x2c\x30\x20\x32\x39\x36\x2e\
515 \x33\x31\x36\x2c\x30\x20\x33\x30\x32\x2c\x31\x30\x2e\x38\x5a\x22\
516 \x20\x73\x74\x79\x6c\x65\x3d\x22\x66\x69\x6c\x6c\x3a\x6e\x6f\x6e\
517 \x65\x3b\x73\x74\x72\x6f\x6b\x65\x3a\x62\x6c\x61\x63\x6b\x3b\x73\
518 \x74\x72\x6f\x6b\x65\x2d\x77\x69\x64\x74\x68\x3a\x32\x2e\x38\x32\
519 \x70\x78\x3b\x22\x2f\x3e\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\
520 \x20\x20\x20\x3c\x2f\x67\x3e\x0a\x20\x20\x20\x20\x20\x20\x20\x20\
521 \x3c\x2f\x67\x3e\x0a\x20\x20\x20\x20\x3c\x2f\x67\x3e\x0a\x3c\x2f\
522 \x73\x76\x67\x3e\x0a\
475 \x00\x00\x02\xd7\
476 \x89\
477 \x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52\x00\
478 \x00\x00\x3c\x00\x00\x00\x3c\x08\x06\x00\x00\x00\x3a\xfc\xd9\x72\
479 \x00\x00\x00\x19\x74\x45\x58\x74\x53\x6f\x66\x74\x77\x61\x72\x65\
480 \x00\x41\x64\x6f\x62\x65\x20\x49\x6d\x61\x67\x65\x52\x65\x61\x64\
481 \x79\x71\xc9\x65\x3c\x00\x00\x02\x79\x49\x44\x41\x54\x78\xda\xec\
482 \x9a\xd1\x6d\xc2\x30\x10\x40\x0d\x62\x80\x6c\x40\x3a\x01\xde\xa0\
483 \x19\x21\x23\xa4\x13\xc0\x06\xf5\x06\xa8\x13\xa4\x1b\xa4\x1b\x44\
484 \x4c\x10\x98\x20\x6c\x10\x36\x68\x8d\xe4\x48\xc8\x75\x82\xef\xb0\
485 \x0f\x87\xf8\xa4\xfb\x31\x89\xc3\xf3\x9d\xed\xbb\xb3\x17\x8c\x56\
486 \xb8\xd4\x5c\xea\x46\x6a\xa2\xda\x2e\x52\x4f\x52\x7f\xa4\x1e\xd9\
487 \x8b\xc8\x15\xb2\x91\xfa\x7b\x47\x5b\xf5\xec\x64\xe5\x6a\xc5\xca\
488 \x02\x54\xd7\xea\xc6\x03\x26\x05\xdb\x20\x60\x7b\x6d\xd4\x14\x98\
489 \x05\x6c\xaf\xdd\x54\x2c\xed\x02\xf6\xd6\xd2\x41\x8b\x70\x08\xdb\
490 \xab\x08\x15\x36\xf5\x00\xdb\x6b\x1a\x22\x70\xe9\x11\xb8\x9e\x93\
491 \x75\x9d\x59\x79\xe9\x10\x78\x4b\x30\xa8\xdb\x90\x2c\xdc\x12\x58\
492 \xb8\x0b\x05\x96\x13\xc0\xf6\xca\x43\x70\xe9\x8c\x38\x2e\x47\xcb\
493 \xea\x0e\x04\x57\x19\xcc\x59\xe9\x90\x6c\x08\x81\x37\x3e\x81\x3f\
494 \xb5\xb6\x2b\xfc\x97\xd4\x6f\xc3\x0a\x4d\xb9\x1b\xe8\x61\x6c\xae\
495 \xda\xfb\xb4\x93\xab\xb4\xf3\x0d\x02\x3c\x34\x57\xd3\x27\xbb\x34\
496 \x1f\xd8\xff\x4d\xf1\x3c\x68\x0e\xbf\x0f\xb4\x87\x96\xa4\x5f\x7c\
497 \x2f\x5a\x17\x36\x61\x59\xb2\x99\xc9\x12\x61\xc9\xf4\x55\x81\x4f\
498 \x13\x01\x4e\x7c\xbb\xf4\xfa\xc9\x0b\xd9\xd1\x62\xd5\x1e\xfc\x4f\
499 \x4b\x40\xc7\x63\x1f\x38\x3f\x71\x55\xe6\x90\x29\x89\x05\x4e\x2c\
500 \xdd\xdf\x87\x1c\x2c\x23\xaf\x03\xa6\xf3\x6e\x20\x80\xcf\x0d\x81\
501 \x07\x55\xf2\x90\x21\xff\xa3\x95\x0c\xd5\x94\x4b\xc0\xe0\xf8\x4c\
502 \x0f\xf3\x91\x67\x51\xd5\xce\x02\xd0\x61\x49\x00\x5c\x5a\x7e\x13\
503 \x5d\xe9\x4c\x98\x7d\x25\x91\x13\xbb\xf3\x58\x49\x69\xf7\xc8\x22\
504 \x51\x31\xfb\x22\x79\x4d\x58\xc4\x2b\x7d\xd5\xbe\x32\x80\x95\xb3\
505 \x00\xac\x5b\xfa\xae\x57\x71\x82\xb9\x5c\x01\x3c\xc9\x49\xaa\x3a\
506 \xb6\x1a\x36\x9a\x6b\x27\x8e\x0b\x7a\xad\xd6\xbf\x60\x44\xb5\xeb\
507 \x1a\xe0\x46\xdc\xd1\x36\xd5\x69\x1e\x94\xfb\x2c\xf0\x41\x2b\x93\
508 \xae\xa1\x75\xd8\x7b\xfd\xed\x7d\x84\x75\x02\x01\x8d\x39\x49\x6c\
509 \x80\xb0\x2d\xf3\x78\xac\x5a\x33\xd8\xe9\x7d\xa2\x06\xaa\xb3\xb4\
510 \xaa\xd0\xde\x2f\x98\xe7\x5a\xb5\x4d\x30\xd2\x58\x2c\x34\x99\xe1\
511 \xbd\x42\x0d\x48\xab\x3d\x5b\xa9\xdf\xf4\x81\xb2\xb9\x2e\x51\x50\
512 \x55\x0e\x3b\xcb\xed\x04\x13\x04\xec\x2c\xfb\x17\x94\x95\x06\xc8\
513 \xa2\x54\x1b\x2c\x68\xea\x6f\x0f\xe8\x13\x15\x60\x2c\x1e\x84\x4e\
514 \x95\x15\x21\x73\xe8\x6c\x28\x18\x40\x83\x85\x0f\xf6\xff\x30\x80\
515 \xb4\xa6\x84\xb9\x9a\x84\xdd\xaa\x82\xb9\xc7\xb5\x63\x7e\x73\xe2\
516 \x9a\x05\x78\xa3\x27\xf1\x10\x4b\x4f\xe2\x76\x5e\xaa\xc0\x1f\xb1\
517 \x78\x43\xb5\xe5\xb8\xb6\x78\xa1\xe0\x5b\x4b\x6b\x0a\x36\xa1\x1b\
518 \x78\xd8\xbc\x9a\x6c\x5f\x8d\x67\x4b\x11\x38\x02\x47\xe0\x08\x1c\
519 \x81\x03\x92\x15\xf2\x3d\x8e\x88\x6b\x6d\x82\x89\x35\x22\x73\x32\
520 \x65\x5f\xe4\x65\x1e\x4a\x15\xd1\xa5\x23\x70\x04\x9e\x8f\xfc\x09\
521 \x30\x00\xa0\x1c\x74\x67\x26\xea\x15\x76\x00\x00\x00\x00\x49\x45\
522 \x4e\x44\xae\x42\x60\x82\
523523 "
524524
525525 qt_resource_name = b"\
527527 \x00\x6f\xa6\x53\
528528 \x00\x69\
529529 \x00\x63\x00\x6f\x00\x6e\x00\x73\
530 \x00\x10\
531 \x08\x12\xae\xa7\
532 \x00\x6d\
533 \x00\x65\x00\x64\x00\x69\x00\x61\x00\x2d\x00\x72\x00\x65\x00\x63\x00\x6f\x00\x72\x00\x64\x00\x2e\x00\x70\x00\x6e\x00\x67\
530 \x00\x0e\
531 \x04\x44\x35\x07\
532 \x00\x63\
533 \x00\x6f\x00\x6c\x00\x6c\x00\x65\x00\x63\x00\x74\x00\x69\x00\x6f\x00\x6e\x00\x2e\x00\x73\x00\x76\x00\x67\
534534 \x00\x07\
535535 \x0a\x7a\x5a\x27\
536536 \x00\x74\
537537 \x00\x61\x00\x67\x00\x2e\x00\x73\x00\x76\x00\x67\
538 \x00\x0e\
539 \x04\x44\x35\x07\
540 \x00\x63\
541 \x00\x6f\x00\x6c\x00\x6c\x00\x65\x00\x63\x00\x74\x00\x69\x00\x6f\x00\x6e\x00\x2e\x00\x73\x00\x76\x00\x67\
542538 \x00\x08\
543539 \x05\x1c\x5a\x47\
544540 \x00\x61\
547543 \x0b\x9e\x57\x87\
548544 \x00\x64\
549545 \x00\x65\x00\x63\x00\x6b\x00\x2e\x00\x73\x00\x76\x00\x67\
546 \x00\x09\
547 \x08\x97\x87\xa7\
548 \x00\x68\
549 \x00\x65\x00\x61\x00\x72\x00\x74\x00\x2e\x00\x73\x00\x76\x00\x67\
550550 \x00\x0c\
551551 \x0e\xcd\x03\x47\
552552 \x00\x6e\
553553 \x00\x6f\x00\x74\x00\x65\x00\x74\x00\x79\x00\x70\x00\x65\x00\x2e\x00\x73\x00\x76\x00\x67\
554 \x00\x09\
555 \x08\x97\x87\xa7\
556 \x00\x68\
557 \x00\x65\x00\x61\x00\x72\x00\x74\x00\x2e\x00\x73\x00\x76\x00\x67\
554 \x00\x10\
555 \x08\x12\xae\xa7\
556 \x00\x6d\
557 \x00\x65\x00\x64\x00\x69\x00\x61\x00\x2d\x00\x72\x00\x65\x00\x63\x00\x6f\x00\x72\x00\x64\x00\x2e\x00\x70\x00\x6e\x00\x67\
558558 "
559559
560560 qt_resource_struct = b"\
561561 \x00\x00\x00\x00\x00\x02\x00\x00\x00\x01\x00\x00\x00\x01\
562562 \x00\x00\x00\x00\x00\x02\x00\x00\x00\x07\x00\x00\x00\x02\
563 \x00\x00\x00\x4a\x00\x01\x00\x00\x00\x01\x00\x00\x07\x0f\
564 \x00\x00\x00\x6c\x00\x00\x00\x00\x00\x01\x00\x00\x09\x7b\
565 \x00\x00\x00\x10\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\
566 \x00\x00\x00\xb6\x00\x00\x00\x00\x00\x01\x00\x00\x1a\xfd\
567 \x00\x00\x00\x36\x00\x00\x00\x00\x00\x01\x00\x00\x02\xdb\
568 \x00\x00\x00\x82\x00\x00\x00\x00\x00\x01\x00\x00\x10\x37\
569 \x00\x00\x00\x98\x00\x00\x00\x00\x00\x01\x00\x00\x15\xa4\
563 \x00\x00\x00\x10\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\
564 \x00\x00\x00\x46\x00\x00\x00\x00\x00\x01\x00\x00\x06\xa0\
565 \x00\x00\x00\xa8\x00\x00\x00\x00\x00\x01\x00\x00\x1c\x2c\
566 \x00\x00\x00\x72\x00\x00\x00\x00\x00\x01\x00\x00\x12\xc9\
567 \x00\x00\x00\x32\x00\x00\x00\x00\x00\x01\x00\x00\x02\x6c\
568 \x00\x00\x00\x5c\x00\x00\x00\x00\x00\x01\x00\x00\x0d\x5c\
569 \x00\x00\x00\x8a\x00\x00\x00\x00\x00\x01\x00\x00\x16\xd3\
570570 "
571571
572572 def qInitResources():
131131 self.actionOpenPluginFolder.setText(_("&Open Add-ons Folder..."))
132132 self.actionDonate.setText(_("&Support Anki..."))
133133 self.actionDownloadSharedPlugin.setText(_("&Browse and Install..."))
134 self.actionFullDatabaseCheck.setText(_("&Check Database..."))
134 self.actionFullDatabaseCheck.setText(_("&Check Database"))
135135 self.actionDocumentation.setText(_("&Guide..."))
136136 self.actionDocumentation.setShortcut(_("F1"))
137 self.actionSwitchProfile.setText(_("&Switch Profile..."))
137 self.actionSwitchProfile.setText(_("&Switch Profile"))
138138 self.actionSwitchProfile.setShortcut(_("Ctrl+Shift+P"))
139139 self.actionExport.setText(_("&Export..."))
140140 self.actionExport.setShortcut(_("Ctrl+E"))
145145 self.actionEmptyCards.setText(_("Empty Cards..."))
146146 self.actionCreateFiltered.setText(_("Create Filtered Deck..."))
147147 self.actionCreateFiltered.setShortcut(_("F"))
148 self.actionNoteTypes.setText(_("Manage Note Types..."))
148 self.actionNoteTypes.setText(_("Manage Note Types"))
149149 self.actionNoteTypes.setShortcut(_("Ctrl+Shift+N"))
150 self.actionAdd_ons.setText(_("Add-ons..."))
150 self.actionAdd_ons.setText(_("Add-ons"))
151151 self.actionAdd_ons.setShortcut(_("Ctrl+Shift+A"))
152152
153153 from . import icons_rc
3333 sizePolicy.setVerticalStretch(0)
3434 sizePolicy.setHeightForWidth(self.lang.sizePolicy().hasHeightForWidth())
3535 self.lang.setSizePolicy(sizePolicy)
36 self.lang.setSizeAdjustPolicy(QtWidgets.QComboBox.AdjustToMinimumContentsLengthWithIcon)
3637 self.lang.setObjectName("lang")
3738 self.horizontalLayout_2.addWidget(self.lang)
3839 self.verticalLayout.addLayout(self.horizontalLayout_2)
364364
365365 def setupApkgImport(mw, importer):
366366 base = os.path.basename(importer.file).lower()
367 full = (base == "collection.apkg") or re.match("backup-.*\\.apkg", base)
367 full = ((base == "collection.apkg") or
368 re.match("backup-.*\\.apkg", base) or
369 base.endswith(".colpkg"))
368370 if not full:
369371 # adding
370372 return True
371373 backup = re.match("backup-.*\\.apkg", base)
372374 if not mw.restoringBackup and not askUser(_("""\
373375 This will delete your existing collection and replace it with the data in \
374 the file you're importing. Are you sure?"""), msgfunc=QMessageBox.warning):
376 the file you're importing. Are you sure?"""), msgfunc=QMessageBox.warning,
377 defaultno=True):
375378 return False
376379 # schedule replacement; don't do it immediately as we may have been
377380 # called as part of the startup routine
210210 def doOpen(path):
211211 self._openBackup(path)
212212 getFile(self.profileDiag, _("Revert to backup"),
213 cb=doOpen, filter="*.apkg", dir=self.pm.backupFolder())
213 cb=doOpen, filter="*.colpkg", dir=self.pm.backupFolder())
214214
215215 def _openBackup(self, path):
216216 try:
279279 for w in self.app.topLevelWidgets():
280280 if w.isVisible():
281281 # windows with this property are safe to close immediately
282 if getattr(w, "silentlyClose"):
282 if getattr(w, "silentlyClose", None):
283283 w.close()
284284 else:
285285 showWarning(f"Window should have been closed: {w}")
365365 Thread.__init__(self)
366366 self.path = path
367367 self.data = data
368 # make sure we complete before exiting the program
369 self.setDaemon(True)
370368 # create the file in calling thread to ensure the same
371369 # file is not created twice
372370 open(self.path, "wb").close()
385383 path = self.pm.collectionPath()
386384
387385 # do backup
388 fname = time.strftime("backup-%Y-%m-%d-%H.%M.%S.apkg", time.localtime(time.time()))
386 fname = time.strftime("backup-%Y-%m-%d-%H.%M.%S.colpkg", time.localtime(time.time()))
389387 newpath = os.path.join(dir, fname)
390388 data = open(path, "rb").read()
391389 b = self.BackupThread(newpath, data)
395393 backups = []
396394 for file in os.listdir(dir):
397395 # only look for new-style format
398 m = re.match("backup-\{4}-.+.apkg", file)
396 m = re.match("backup-\d{4}-\d{2}-.+.colpkg", file)
399397 if not m:
400398 continue
401399 backups.append(file)
785783 aqt.dialogs.open("DeckStats", self)
786784
787785 def onPrefs(self):
788 import aqt.preferences
789 aqt.preferences.Preferences(self)
786 aqt.dialogs.open("Preferences", self)
790787
791788 def onNoteTypes(self):
792789 import aqt.models
106106 newPath = os.path.join(_exportFolder, path[len(targetPath)+1:])
107107 return newPath
108108 return path
109
110 # work around Windows machines with incorrect mime type
111 RequestHandler.extensions_map['.css'] = "text/css"
2121 self.form.buttonBox.button(QDialogButtonBox.Help).setAutoDefault(False)
2222 self.form.buttonBox.button(QDialogButtonBox.Close).setAutoDefault(False)
2323 self.form.buttonBox.helpRequested.connect(lambda: openHelp("profileprefs"))
24 self.silentlyClose = True
2425 self.setupLang()
2526 self.setupCollection()
2627 self.setupNetwork()
3940 self.mw.pm.save()
4041 self.mw.reset()
4142 self.done(0)
43 aqt.dialogs.markClosed("Preferences")
4244
4345 def reject(self):
4446 self.accept()
166166 playFromText(q)
167167 # render & update bottom
168168 q = self._mungeQA(q)
169 q = runFilter("prepareQuestion", q)
169 q = runFilter("prepareQA", q, c, "reviewQuestion")
170170
171171 bodyclass = "card card%d" % (c.ord+1)
172172
210210 if self.autoplay(c):
211211 playFromText(a)
212212 a = self._mungeQA(a)
213 a = runFilter("prepareAnswer", a)
213 a = runFilter("prepareQA", a, c, "reviewAnswer")
214214 # render and update bottom
215215 self.web.eval("_showAnswer(%s);" % json.dumps(a))
216216 self._showEaseButtons()
44 from aqt.qt import *
55 import os, time
66 from aqt.utils import saveGeom, restoreGeom, maybeHideClose, addCloseShortcut, \
7 tooltip
7 tooltip, getSaveFile
88 import aqt
99
1010 # Deck Stats
5454 name = time.strftime("-%Y-%m-%d@%H-%M-%S.pdf",
5555 time.localtime(time.time()))
5656 name = "anki-"+_("stats")+name
57 desktopPath = QStandardPaths.writableLocation(
58 QStandardPaths.DesktopLocation)
59 if not os.path.exists(desktopPath):
60 os.mkdir(desktopPath)
61 path = os.path.join(desktopPath, name)
62 return path
57 file = getSaveFile(self, title=_("Save PDF"),
58 dir_description="stats",
59 key="stats",
60 ext=".pdf",
61 fname=name)
62 return file
6363
6464 def saveImage(self):
6565 path = self._imagePath()
66 if not path:
67 return
6668 self.form.web.page().printToPdf(path)
67 tooltip(_("A PDF file was saved to your desktop."))
69 tooltip(_("Saved."))
6870
6971 def changePeriod(self, n):
7072 self.period = n
6565 diag.setWindowTitle(title)
6666 layout = QVBoxLayout(diag)
6767 diag.setLayout(layout)
68 text = QTextEdit()
69 text.setReadOnly(True)
68 text = QTextBrowser()
69 text.setOpenExternalLinks(True)
7070 if type == "text":
7171 text.setPlainText(txt)
7272 else:
278278 """Ask the user for a file to save. Use DIR_DESCRIPTION as config
279279 variable. The file dialog will default to open with FNAME."""
280280 config_key = dir_description + 'Directory'
281 base = aqt.mw.pm.profile.get(config_key, aqt.mw.pm.base)
281
282 defaultPath = QStandardPaths.writableLocation(QStandardPaths.DocumentsLocation)
283 base = aqt.mw.pm.profile.get(config_key, defaultPath)
282284 path = os.path.join(base, fname)
283285 file = QFileDialog.getSaveFileName(
284286 parent, title, path, "{0} (*{1})".format(key, ext),
7676 <string>New Cards</string>
7777 </attribute>
7878 <layout class="QVBoxLayout" name="verticalLayout_2">
79 <property name="margin">
80 <number>12</number>
81 </property>
7982 <item>
8083 <layout class="QGridLayout" name="gridLayout">
84 <property name="spacing">
85 <number>12</number>
86 </property>
8187 <item row="5" column="2">
8288 <widget class="QLabel" name="label_27">
8389 <property name="text">
220226 <string>Reviews</string>
221227 </attribute>
222228 <layout class="QVBoxLayout" name="verticalLayout_4">
229 <property name="margin">
230 <number>12</number>
231 </property>
223232 <item>
224233 <layout class="QGridLayout" name="gridLayout_3">
234 <property name="spacing">
235 <number>12</number>
236 </property>
225237 <item row="1" column="0">
226238 <widget class="QLabel" name="label_20">
227239 <property name="text">
359371 <string>Lapses</string>
360372 </attribute>
361373 <layout class="QVBoxLayout" name="verticalLayout_3">
374 <property name="margin">
375 <number>12</number>
376 </property>
362377 <item>
363378 <layout class="QGridLayout" name="gridLayout_2">
379 <property name="spacing">
380 <number>12</number>
381 </property>
364382 <item row="0" column="0">
365383 <widget class="QLabel" name="label_17">
366384 <property name="text">
502520 <string>General</string>
503521 </attribute>
504522 <layout class="QVBoxLayout" name="verticalLayout_6">
523 <property name="margin">
524 <number>12</number>
525 </property>
505526 <item>
506527 <layout class="QGridLayout" name="gridLayout_5">
528 <property name="spacing">
529 <number>12</number>
530 </property>
507531 <item row="0" column="0">
508532 <widget class="QLabel" name="label_25">
509533 <property name="text">
577601 <string>Description</string>
578602 </attribute>
579603 <layout class="QVBoxLayout" name="verticalLayout_5">
604 <property name="spacing">
605 <number>12</number>
606 </property>
607 <property name="margin">
608 <number>12</number>
609 </property>
580610 <item>
581611 <widget class="QLabel" name="label_22">
582612 <property name="text">
164164 </action>
165165 <action name="actionFullDatabaseCheck">
166166 <property name="text">
167 <string>&amp;Check Database...</string>
167 <string>&amp;Check Database</string>
168168 </property>
169169 </action>
170170 <action name="actionDocumentation">
177177 </action>
178178 <action name="actionSwitchProfile">
179179 <property name="text">
180 <string>&amp;Switch Profile...</string>
180 <string>&amp;Switch Profile</string>
181181 </property>
182182 <property name="shortcut">
183183 <string>Ctrl+Shift+P</string>
222222 </action>
223223 <action name="actionNoteTypes">
224224 <property name="text">
225 <string>Manage Note Types...</string>
225 <string>Manage Note Types</string>
226226 </property>
227227 <property name="shortcut">
228228 <string>Ctrl+Shift+N</string>
230230 </action>
231231 <action name="actionAdd_ons">
232232 <property name="text">
233 <string>Add-ons...</string>
233 <string>Add-ons</string>
234234 </property>
235235 <property name="shortcut">
236236 <string>Ctrl+Shift+A</string>
4848 <horstretch>0</horstretch>
4949 <verstretch>0</verstretch>
5050 </sizepolicy>
51 </property>
52 <property name="sizeAdjustPolicy">
53 <enum>QComboBox::AdjustToMinimumContentsLengthWithIcon</enum>
5154 </property>
5255 </widget>
5356 </item>
1616 # set flag 2
1717 col.setUserFlag(2, [c.id])
1818 c.load()
19 print("db is", col.db.all("select id, flags from cards"))
2019 assert c.userFlag() == 2
2120 assert c.flags & origBits == origBits
2221 assert len(col.findCards("flag:0")) == 0
257257 }
258258 }
259259
260 function onCutOrCopy() {
261 pycmd("cutOrCopy");
262 return true;
263 }
264
260265 function setFields(fields, prewrap) {
261266 var txt = "";
262267 for (var i = 0; i < fields.length; i++) {
269274 txt += "<div id=f{0} onkeydown='onKey();' oninput='checkForEmptyField()' onmouseup='onKey();'".format(i);
270275 txt += " onfocus='onFocus(this);' onblur='onBlur();' class=field ";
271276 txt += "ondragover='onDragOver(this);' onpaste='onPaste(this);' ";
277 txt += "oncopy='onCutOrCopy(this);' oncut='onCutOrCopy(this);' ";
272278 txt += "contentEditable=true class=field>{0}</div>".format(f);
273279 txt += "</td></tr>";
274280 }
22 extensions: ["tex2jax.js"],
33 TeX: {
44 extensions: ["AMSmath.js","AMSsymbols.js","noErrors.js","noUndefined.js", "mhchem.js"]
5 },
6 tex2jax: {
7 displayMath: [ ["\\[","\\]"] ],
58 },
69 messageStyle: "none",
710 skipStartupTypeset: true,
2525 } catch(err) {
2626 qa.text("Invalid HTML on card: "+err);
2727 }
28 _removeStylingFromMathjaxCloze();
2928 _runHook(onUpdateHook);
3029
3130 // don't allow drags of images, which cause them to be deleted
105104 pycmd("ans");
106105 }
107106 }
108
109 function _removeStylingFromMathjaxCloze() {
110 $(".cloze").each(function (i) {
111 if (_clozeIsInsideMathjax(this)) {
112 this.outerHTML = this.innerHTML;
113 }
114 });
115 }
116
117 function _clozeIsInsideMathjax(node) {
118 if (!node.previousSibling || node.previousSibling.nodeType !== 3) {
119 return;
120 }
121 // look for mathjax opening in previous text
122 return /\\\(|\$\$/.test(node.previousSibling.textContent);
123 }