def reaction(self, *connection_strings): # The JS version (no decorator functionality) if len(connection_strings) < 2: raise RuntimeError( 'Component.reaction() (js) needs a function and ' 'one or more connection strings.') # Get callable if callable(connection_strings[0]): func = connection_strings[0] connection_strings = connection_strings[1:] elif callable(connection_strings[-1]): func = connection_strings[-1] connection_strings = connection_strings[:-1] else: raise TypeError('Component.reaction() requires a callable.') # Verify connection strings for i in range(len(connection_strings)): s = connection_strings[i] if not (isinstance(s, str) and len(s)): raise ValueError('Connection string must be nonempty strings.') # Get function name (Flexx sets __name__ on methods) name = RawJS("func.__name__ || func.name || 'anonymous'") # name = name.split(' ')[-1].split('flx_')[-1] nameparts = RawJS("name.split(' ')") nameparts = RawJS("nameparts[nameparts.length-1].split('flx_')") name = nameparts[-1] mode = 'normal' return self.__create_reaction_ob(func, name, mode, connection_strings)
def foo(a, b): x = RawJS('a + b') y = 0 RawJS(""" for (i=0; i<8; i++) { y += i * x; } """) RawJS("""while (y>0) { x += y; y -= 1; } """)
def _validate_browser_capabilities(self): # We test a handful of features here, and assume that if these work, # all of Flexx works. It is not a hard guarantee, of course, because # the user can use modern features in an application. RawJS(""" var el = window.document.getElementById('flexx-spinner'); if ( window.WebSocket === undefined || // IE10+ Object.keys === undefined || // IE9+ false ) { var msg = ('Flexx does not support this browser.<br>' + 'Try Firefox, Chrome, ' + 'or a more recent version of the current browser.'); if (el) { el.children[0].innerHTML = msg; } else { window.alert(msg); } return false; } else if (''.startsWith === undefined) { // probably IE var msg = ('Flexx support for this browser is limited.<br>' + 'Consider using Firefox, Chrome, or maybe Edge.'); if (el) { el.children[0].innerHTML = msg; } return true; } else { return true; } """)
def __create_reaction_ob(self, reaction_func, name, mode, connection_strings): # Keep ref to the reaction function, see comment in create_action(). # Create function that becomes our "reaction object" def reaction(): return reaction_func.apply(self, arguments) # arguments == events # Attach methods to the function object (this gets replaced) REACTION_METHODS_HOOK # noqa # Init reaction that = self RawJS("Component.prototype._REACTION_COUNT += 1") reaction._id = RawJS("'r' + Component.prototype._REACTION_COUNT") reaction._name = name reaction._mode = mode reaction._ob1 = lambda : that # no weakref in JS reaction._init(connection_strings, self) return reaction
def spin(self, n=1): RawJS(""" if (!window.document.body) {return;} var el = window.document.body.children[0]; if (el && el.classList.contains("flx-spinner")) { if (n === null) { el.style.display = 'none'; // Stop the spinner } else { el.children[0].innerHTML += '■'.repeat(n); } } """)
def _create_pointer_event(self, e): # Get offset to fix positions rect = self.node.getBoundingClientRect() offset = rect.left, rect.top if e.type.startswith('touch'): # Touch event - select one touch to represent the main position t = e.changedTouches[0] pos = float(t.clientX - offset[0]), float(t.clientY - offset[1]) page_pos = t.pageX, t.pageY button = 0 buttons = [] # Include basic support for multi-touch touches = {} for i in range(e.changedTouches.length): t = e.changedTouches[i] if t.target is not e.target: continue touches[t.identifier] = (float(t.clientX - offset[0]), float(t.clientY - offset[1]), t.force) else: # Mouse event pos = float(e.clientX - offset[0]), float(e.clientY - offset[1]) page_pos = e.pageX, e.pageY # Fix buttons if e.buttons: buttons_mask = RawJS( "e.buttons.toString(2).split('').reverse().join('')") else: # libjavascriptcoregtk-3.0-0 version 2.4.11-1 does not define # e.buttons buttons_mask = [e.button.toString(2)] buttons = [i + 1 for i in range(5) if buttons_mask[i] == '1'] button = {0: 1, 1: 3, 2: 2, 3: 4, 4: 5}[e.button] touches = { -1: (pos[0], pos[1], 1) } # key must not clash with real touches # note: our button has a value as in JS "which" modifiers = [ n for n in ('Alt', 'Shift', 'Ctrl', 'Meta') if e[n.toLowerCase() + 'Key'] ] # Create event dict return dict( pos=pos, page_pos=page_pos, touches=touches, button=button, buttons=buttons, modifiers=modifiers, )
def __init__(self, *init_args, **property_values): RawJS('Component.prototype._COUNT += 1') self._id = RawJS("this.__name__ + Component.prototype._COUNT") self._disposed = False # Init some internal variables self.__handlers = {} # reactions connecting to this component self.__pending_events = [] self.__anonymous_reactions = [] self.__initial_mutation = False # Create actions for i in range(len(self.__actions__)): name = self.__actions__[i] self.__create_action(self[name], name) # Create emitters for i in range(len(self.__emitters__)): name = self.__emitters__[i] self.__handlers[name] = [] self.__create_emitter(self[name], name) # Create properties for i in range(len(self.__properties__)): name = self.__properties__[i] self.__handlers[name] = [] self.__create_property(name) # Create attributes for i in range(len(self.__attributes__)): name = self.__attributes__[i] self.__create_attribute(name) # With self as the active component (and thus mutatable), init the # values of all properties, and apply user-defined initialization with self: self._comp_init_property_values(property_values) self.init(*init_args) # Connect reactions and fire initial events self._comp_init_reactions()
def spin(self, n=1): RawJS(""" var el = window.document.getElementById('flexx-spinner'); if (el) { if (n === null) { // Hide the spinner overlay, now or in a bit if (el.children[0].innerHTML.indexOf('limited') > 0) { setTimeout(function() { el.style.display = 'none'; }, 2000); } else { el.style.display = 'none'; } } else { for (var i=0; i<n; i++) { el.children[1].innerHTML += '■'; } } } """)
def get_weeknumber(t): """Get the ISO 8601 week number.""" # From https://weeknumber.net/how-to/javascript date = Date(t * 1000) # noqa RawJS(""" date.setHours(0, 0, 0, 0); // Thursday in current week decides the year. date.setDate(date.getDate() + 3 - (date.getDay() + 6) % 7); // January 4 is always in week 1. var week1 = new Date(date.getFullYear(), 0, 4); // Adjust to Thursday in week 1 and count number of weeks from date to week1. var res = 1 + Math.round(((date.getTime() - week1.getTime()) / 86400000 - 3 + (week1.getDay() + 6) % 7) / 7); """) return res # noqa
def keep_checking_size_of(self, ob, check=True): """ This is a service that the session provides. """ # Gets called by the Widget class for toplevel widgets. That # is, toplevel to Flexx: they might not be toplevel for the # browser. This method will make sure that they know their size # in any case, at least once each second. if check: self.instances_to_check_size[ob.id] = ob else: self.instances_to_check_size.pop(ob.id, None) def _check_size_of_objects(self): for ob in self.instances_to_check_size.values(): if ob._disposed is False: ob.check_real_size() # In Python, we need some extras for the serializer to work if this_is_js(): # Include bsdf.js window.flexx = Flexx() bsdf = RawJS("flexx.require('bsdf')") serializer = bsdf.BsdfSerializer() window.flexx.serializer = serializer else: # Import vendored bsdf lite module from . import bsdf_lite as bsdf serializer = bsdf.BsdfLiteSerializer() serializer.__module__ = __name__
def test_raw_js(): with raises(TypeError): RawJS() with raises(TypeError): RawJS(3) # Empty r1 = RawJS('') assert str(r1) == '' assert r1.get_code() == '' assert r1.get_code(4) == '' assert '0' in repr(r1) assert r1.__module__.endswith(__name__) # Short single line r2 = RawJS('require("foobar")') assert 'require(' in repr(r2) assert 'require(' in str(r2) assert r2.get_code().startswith('require') assert r2.get_code(4).startswith(' require') assert r2.get_code(2).startswith(' require') assert '\n' not in r2.get_code() # Long single line r2b = RawJS('require("foobar")'*10) assert 'require(' not in repr(r2b) assert '1' in repr(r2b) # Multiline, start at first line r3 = RawJS("""for ... { yyyy } """) assert 'lines' in repr(r3) assert 'for ...' in str(r3) assert str(r3).endswith('}\n') assert r3.get_code().count('\n') == 3 assert r3.get_code().startswith('for') assert r3.get_code(4).startswith(' for') assert '\n yyyy\n' in r3.get_code(0) assert '\n yyyy\n' in r3.get_code(4) # Multiline, exactly the same, but start at second line; same results r4 = RawJS(""" for ... { yyyy } """) assert 'lines' in repr(r4) assert 'for ...' in str(r4) assert str(r4).endswith('}\n') assert r4.get_code().count('\n') == 3 assert r4.get_code().startswith('for') assert r4.get_code(4).startswith(' for') assert '\n yyyy\n' in r4.get_code(0) assert '\n yyyy\n' in r4.get_code(4) # Multiline, now newline at the ned r5 = RawJS(""" for ... { yyyy }""") assert r5.get_code().count('\n') == 2 assert str(r5).endswith('}')
def _create_dom(self): global window node = window.document.createElement('input') RawJS('$')(node).datepicker() return node
def func(a, b): RawJS(""" var c = 3; return a + b + c; """)
def load_viz(self): w, h = self.size nodes = d3.range(200).map(lambda: {'radius': Math.random() * 12 + 4}) color = d3.scale.category10() force = d3.layout.force().gravity(0.05).charge(lambda d, i: 0 if i else -2000)\ .nodes(nodes).size([w, h]) root = nodes[0] root.radius = 0 root.fixed = True force.start() x = d3.select('#' + self.id) print(x, self.id) svg = RawJS('x.append("svg").attr("width", w).attr("height", h)') x = RawJS( 'svg.selectAll("circle").data(nodes.slice(1)).enter().append("circle")') x.attr("r", lambda d: d.radius).style("fill", lambda d, i: color(i % 3)) def on_tick(e): q = d3.geom.quadtree(nodes) i = 0 n = nodes.length while i < n-1: i += 1 q.visit(collide(nodes[i])) svg.selectAll("circle").attr("cx", lambda d: d.x).attr("cy", lambda d: d.y) force.on("tick", on_tick) def on_mousemove(): p1 = d3.mouse(self.node) root.px = p1[0] root.py = p1[1] force.resume() svg.on("mousemove", on_mousemove) def collide(node): r = node.radius + 16 nx1 = node.x - r nx2 = node.x + r ny1 = node.y - r ny2 = node.y + r def func(quad, x1, y1, x2, y2): if quad.point and quad.point is not node: x = node.x - quad.point.x y = node.y - quad.point.y s = Math.sqrt(x * x + y * y) r = node.radius + quad.point.radius if (s < r): s = (s - r) / s * .5 x *= s y *= s node.x -= x node.y -= y quad.point.x += x quad.point.y += y return x1 > nx2 or x2 < nx1 or y1 > ny2 or y2 < ny1 return func