#!/usr/bin/env python3 # -*- coding:utf-8 -*- from pathlib import Path from pscript import RawJS from flexx import flx from ipfsdocs.grid import Grid from ipfsdocs.slide import Slide from ipfsdocs.log import info from ipfsdocs.header import Header from ipfsdocs.footer import Footer from ipfsdocs.modals import ( MoveModal, TagModal, SetTagModal, SetOwnerModal, SetDateModal, ConfirmModal, ShowSharedModal, ShowModal, OptionsModal, ) from ipfsdocs.common import (set_to_localstorage, get_from_localstorage) def import_assets(): def add_asset(url): return flx.assets.associate_asset( __name__, url, ) def add_js(path): path = Path(__file__).parent / "js" / path return flx.assets.associate_asset(__name__, path.name, path.read_text()) def add_css(path): path = Path(__file__).parent / "css" / path return flx.assets.associate_asset(__name__, path.name, path.read_text()) def add_data(path): path = Path(__file__).parent / "assets" / path return flx.assets.add_shared_data(path.name, path.read_bytes()) # add_asset("https://stackpath.bootstrapcdn.com/bootstrap/4.5.0/css/bootstrap.min.css") add_css("bootstrap.min.css") # add_asset("https://code.jquery.com/jquery-3.5.1.js") add_js("jquery-3.5.1.js") # add_asset("https://stackpath.bootstrapcdn.com/bootstrap/4.5.0/js/bootstrap.bundle.min.js") add_js("bootstrap.bundle.min.js") # add_asset("http://hammerjs.github.io/dist/hammer.min.js") add_js("hammer.min.js") # add_asset("https://cdnjs.cloudflare.com/ajax/libs/toastr.js/latest/toastr.min.js") add_js("toastr.min.js") # add_asset("https://cdnjs.cloudflare.com/ajax/libs/toastr.js/latest/toastr.min.css") add_css("toastr.min.css") # add_asset("https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.27.0/moment-with-locales.min.js") add_js("moment-with-locales.min.js") # add_asset("https://cdnjs.cloudflare.com/ajax/libs/bootstrap-datepicker/1.9.0/js/bootstrap-datepicker.min.js") add_js("bootstrap-datepicker.min.js") # add_asset("https://cdnjs.cloudflare.com/ajax/libs/bootstrap-datepicker/1.9.0/css/bootstrap-datepicker.min.css") add_css("bootstrap-datepicker.min.css") add_js("qrcode.js") add_js("keypress.js") add_js("download.js") add_js("wheelzoom.js") global fonts fonts = { fontname: flx.assets.add_shared_data(fontname, (Path(__file__).parent / "fonts" / fontname).read_bytes()) for fontname in [ "FontAwesome.otf", "fontawesome-webfont.eot", "fontawesome-webfont.svg", "fontawesome-webfont.ttf", "fontawesome-webfont.woff", "fontawesome-webfont.woff2", ] } add_css("font-awesome.css") flx.assets.associate_asset( __name__, "ipfsdocs-fonts.css", f""" @font-face {{ font-family: 'FontAwesome'; src: url('{fonts["fontawesome-webfont.eot"]}'); src: url('{fonts["fontawesome-webfont.eot"]}') format('embedded-opentype'), url('{fonts["fontawesome-webfont.woff2"]}') format('woff2'), url('{fonts["fontawesome-webfont.woff"]}') format('woff'), url('{fonts["fontawesome-webfont.ttf"]}') format('truetype'), url('{fonts["fontawesome-webfont.svg"]}') format('svg'); font-weight: normal; font-style: normal; }} @font-face {{ font-family: 'Handlee'; font-style: normal; font-weight: 400; font-display: swap; src: local('Handlee Regular'), local('Handlee-Regular'), url(https://fonts.gstatic.com/s/handlee/v8/-F6xfjBsISg9aMakDmo.ttf) format('truetype'); }} @font-face {{ font-family: 'Permanent Marker'; font-style: normal; font-weight: 400; font-display: swap; src: local('Permanent Marker Regular'), local('PermanentMarker-Regular'), url(https://fonts.gstatic.com/s/permanentmarker/v9/Fh4uPib9Iyv2ucM6pGQMWimMp004Hao.ttf) format('truetype'); }} """) flx.assets.associate_asset( __name__, "ipfsdocs-common.css", """ body { font-family: "FontAwesome", "Handlee", cursive; } .centered { margin: auto; top: 0; bottom: 0; left: 0; right: 0; position: absolute; } .discret { opacity: 0.2; } .shrink_to_space { max-height: 100%; max-width: 100%; } .auto-margin { margin: auto!important; } .fit { object-fit: contain; width: 100%; height: 100%; } .fullscreen { position: fixed; top: 0; left: 0; width: 100vw; height: 100vh; } """, ) add_js("RadialMenu.js") global MANIFEST MANIFEST = add_data("manifest.js") import_assets() class Body(flx.TabLayout): CSS = """ .flx-Body { background-color: black; } .flx-Body > .flx-tabbar > .flx-tab-item { border-top: 0; padding-top: 0; } """ current_index = flx.IntProp(settable=True) def init(self): self.grid = Grid(title="\uf009") self.slide = Slide(title="\uf096 \uf061 \uf0c8 \uf061 \uf096") with self.grid: self.showshared = ShowSharedModal() self.options_handler = OptionsModal() self.showmodal = ShowModal() self.tag_modal = TagModal() self.confirm = ConfirmModal() self.initialized = False @flx.reaction("current") def do_initialization(self): if not self.initialized: self.set_current(self.children[get_from_localstorage("current_index")["current_index"] or 0]) self.initialized = True @flx.reaction("current") def update_toastr_target(self, *event): global toastr, RadialMenu if self.current is not None: toastr.options.target = self.current.node RadialMenu.element = self.current.node self.showshared.set_parent(self.current) self.move_handler.set_parent(self.current) self.options_handler.set_parent(self.current) self.showmodal.set_parent(self.current) self.tag_modal.set_parent(self.current) self.confirm.set_parent(self.current) @flx.reaction("current_index") def save_current_index(self): set_to_localstorage("current_index", {"current_index": self.current_index}) @flx.reaction("current") def update_current_index(self): if self.current is not None: self.set_current_index(self.children.index(self.current)) @flx.reaction("current") def scroll_slide_when_visible(self, *events): if self.current == self.slide: self.slide.scroll_to_current_index() @flx.reaction("current", "pointer_click") def focus_on_the_current_widget(self): if self.root.frontend and self.root.frontend.inmodal: return if self.current and self.current.node: self.current.node.focus() @flx.reaction("root.pysays.shared_one", "root.pysays.shared_selection") def display_shared(self, *events): for event in events: self.showshared.set_cid(event.url) self.showshared.set_webcid(event.weburl) self.showshared.display() @flx.reaction("current.placeholders*.pointer_click") def update_range(self, *events): if self.root.frontend.range_mode: event = events[-1] cid = event.source.cid if self.root.frontend.cid_extremum == "": self.root.frontend.set_cid_extremum(cid) else: entered = False for info_ in self.root.state.query_results: if info_["cid"] in (cid, self.root.frontend.cid_extremum): entered = not entered if entered is True: self.root.state.add_to_selection(info_["cid"]) self.root.frontend.set_cid_extremum("") class Frontend(flx.VBox): selection_only = flx.BoolProp(False, settable=True) inmodal = flx.BoolProp(settable=True) range_mode = flx.BoolProp(settable=True) cid_extremum = flx.StringProp(settable=True) def init(self): global RadialMenu, keypress, moment self.header = Header() self.body = Body(flex=1) self.move_handler = self.body.move_handler self.options_handler = self.body.options_handler self.showmodal = self.body.showmodal self.tag_modal = self.body.tag_modal self.confirm = self.body.confirm self.set_tag_modal = SetTagModal() self.set_owner_modal = SetOwnerModal() self.set_date_modal = SetDateModal() self.footer = Footer(flex=0.0001) listener = keypress.Listener() listener.simple_combo("f", lambda: self.enable_fullscreen()) # the first instance does not contain the icons, so here is a dummy # instance, just to make the next ones work well RadialMenu() moment.locale("fr") global window manifest = window.document.createElement('link') manifest["rel"] = 'manifest' manifest["href"] = "/" + MANIFEST global document document.getElementsByTagName('head')[0].appendChild(manifest) global toastr toastr.options.progressBar = True toastr.options.positionClass = "toast-top-center" toastr.options.preventDuplicates = False toastr.options.newestOnTop = False @flx.reaction("root.state.readonly") def hide_when_readonly(self): self.header.dispose() @flx.reaction("footer.navigation.pointer_click") def focus_on_the_current_widget(self): self.body.focus_on_the_current_widget() @flx.reaction("root.state.query_results") def clean_cid_extremum(self): self.set_cid_extremum("") @flx.emitter def download(self, links): return {"links": links} @flx.reaction("root.pysays.ok") def sayok(self): info("\uf058") @flx.reaction("root.jssays.fullscreen") def enable_fullscreen(self, *events): if self.root.frontend and self.root.frontend.inmodal: # return True for keypress to avoid preventing the default behavior return True elem = self.body.current.node for method in [ "requestFullscreen", "mozRequestFullScreen", "webkitRequestFullscreen", "msRequestFullscreen", ]: if hasattr(elem, method): # to understand why the need for bind -> https://code-examples.net/en/q/a3ef2c getattr(elem, method).bind(elem)() break self.body.current.node.focus() # moving to fullscreen may change the size, but flexx won't catch it self.body.current.check_real_size() class FrontendSlide(flx.VBox): selection_only = flx.BoolProp(False, settable=True) inmodal = flx.BoolProp(settable=True) range_mode = flx.BoolProp(settable=True) cid_extremum = flx.StringProp(settable=True) def init(self): global RadialMenu, keypress, moment with Slide(flex=1, title="\uf096 \uf061 \uf0c8 \uf061 \uf096") as self.slide: self.showshared = ShowSharedModal() self.options_handler = OptionsModal() self.showmodal = ShowModal() self.tag_modal = TagModal() self.confirm = ConfirmModal() self.set_owner_modal = SetOwnerModal() self.set_date_modal = SetDateModal() self.root.state.set_from_date("2015-10-30") self.root.state.set_owner("AYLAPOMME") self.root.state.set_state("DONE") self.root.state.set_autoplay(True) listener = keypress.Listener() listener.simple_combo("f", lambda: self.enable_fullscreen()) # the first instance does not contain the icons, so here is a dummy # instance, just to make the next ones work well RadialMenu() moment.locale("fr") global window manifest = window.document.createElement('link') manifest["rel"] = 'manifest' manifest["href"] = "/" + MANIFEST global document document.getElementsByTagName('head')[0].appendChild(manifest) global toastr toastr.options.progressBar = True toastr.options.positionClass = "toast-top-center" toastr.options.preventDuplicates = False toastr.options.newestOnTop = False @flx.reaction("root.state.query_results") def clean_cid_extremum(self): self.set_cid_extremum("") @flx.emitter def download(self, links): return {"links": links} @flx.reaction("root.pysays.ok") def sayok(self): info("\uf058")