Dethe Elza
    • Create new note
    • Create a note from template
      • Sharing URL Link copied
      • /edit
      • View mode
        • Edit mode
        • View mode
        • Book mode
        • Slide mode
        Edit mode View mode Book mode Slide mode
      • Customize slides
      • Note Permission
      • Read
        • Only me
        • Signed-in users
        • Everyone
        Only me Signed-in users Everyone
      • Write
        • Only me
        • Signed-in users
        • Everyone
        Only me Signed-in users Everyone
      • Engagement control Commenting, Suggest edit, Emoji Reply
      • Invitee
    • Publish Note

      Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note

      Your note will be visible on your profile and discoverable by anyone.
      Your note is now live.
      This note is visible on your profile and discoverable online.
      Everyone on the web can find and read all notes of this public team.
      See published notes
      Unpublish note
      Please check the box to agree to the Community Guidelines.
      View profile
    • Commenting
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
      • Everyone
    • Suggest edit
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
    • Emoji Reply
    • Enable
    • Versions and GitHub Sync
    • Note settings
    • Engagement control
    • Transfer ownership
    • Delete this note
    • Save as template
    • Insert from template
    • Import from
      • Dropbox
      • Google Drive
      • Gist
      • Clipboard
    • Export to
      • Dropbox
      • Google Drive
      • Gist
    • Download
      • Markdown
      • HTML
      • Raw HTML
Menu Note settings Sharing URL Create Help
Create Create new note Create a note from template
Menu
Options
Versions and GitHub Sync Engagement control Transfer ownership Delete this note
Import from
Dropbox Google Drive Gist Clipboard
Export to
Dropbox Google Drive Gist
Download
Markdown HTML Raw HTML
Back
Sharing URL Link copied
/edit
View mode
  • Edit mode
  • View mode
  • Book mode
  • Slide mode
Edit mode View mode Book mode Slide mode
Customize slides
Note Permission
Read
Only me
  • Only me
  • Signed-in users
  • Everyone
Only me Signed-in users Everyone
Write
Only me
  • Only me
  • Signed-in users
  • Everyone
Only me Signed-in users Everyone
Engagement control Commenting, Suggest edit, Emoji Reply
Invitee
Publish Note

Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note

Your note will be visible on your profile and discoverable by anyone.
Your note is now live.
This note is visible on your profile and discoverable online.
Everyone on the web can find and read all notes of this public team.
See published notes
Unpublish note
Please check the box to agree to the Community Guidelines.
View profile
Engagement control
Commenting
Permission
Disabled Forbidden Owners Signed-in users Everyone
Enable
Permission
  • Forbidden
  • Owners
  • Signed-in users
  • Everyone
Suggest edit
Permission
Disabled Forbidden Owners Signed-in users Everyone
Enable
Permission
  • Forbidden
  • Owners
  • Signed-in users
Emoji Reply
Enable
Import from Dropbox Google Drive Gist Clipboard
   owned this note    owned this note      
Published Linked with GitHub
Subscribed
  • Any changes
    Be notified of any changes
  • Mention me
    Be notified of mention me
  • Unsubscribe
Subscribe
# Reimplementing Waterbear # Waterbear vs. Bloc vs. Waterbear 2.0 ###### tags: `sketchdance` ## Waterbear 2.0 Note: These [Suggestions for Scratch](https://scratch.mit.edu/discuss/topic/382706/) can be a valuable resource for Waterbear. * [x] Get a basic step block using Heresy * [x] Parameter blocks too * [x] Color Value * [x] Number Value * [x] Text Value * [x] Generate parameters from signature? * [x] Generate steps from Moonshine AST * [x] Implement list types in Moonshine * [x] Simplify Moonshine syntax? * [x] Use details/summary disclosures for block menu * [x] Fix up block layout/styling (disclosure triangle placement, stacking, padding) * [x] Fix bug in context generation for contexts in Moonshine * [x] Need more syntax to distinguish steps/contexts/values to make Moonshine more readable * [x] Contexts need locals, not form fields * [x] Implement trigger blocks * [x] Generate contexts from AST * [x] Generate triggers from AST * [x] Icons for types on inputs * [x] Implement value blocks * [x] Generate values from AST * [x] Avoid built-in number input * [x] Avoid built-in color input * [x] Icons for types on blocks * [x] Implement local blocks * [x] Implement returns blocks * [x] Styling fixes * [x] Structure page so there is somewhere to drag *to* * [x] Fix problem with cloning elements (when I start dragging it breaks) * [ ] Override .cloneNode on custom elements * [ ] Use dragula.js to reimplement drag and drop, see [Dragging Rules for Waterbear](/wZ0wjizjS0uKpUSHJrFbkw) * [ ] Basics working * [ ] Handles scope * [ ] On Program Start implemented * [ ] setValue implemented * [ ] deferred types propagating * [ ] dynamic renaming * [ ] Auto-wiring * [ ] Mark out-of-scope blocks as invalid * [x] Style page, especially blockmenu headings * [ ] Contexts should have a return socket to make the return value explicit * [ ] Track all namespaces while building menus * [ ] Track all types while building menus * [ ] Use Lea Verou's https://projects.verou.me/stretchy/ vs. roll-our-own input resizing * [ ] Localization for blocks * [ ] Preserve comments? * [ ] If/Then as a special case * [ ] Update AST from drag results * [ ] Integrate AST with Immer * [ ] How do I build structs? ## Inserting items This includes behaviour of drag-and-drop, but also of click-to-insert, undo/redo, cut/copy/paste, and delete. * A container (context, trigger, wb-contains, etc.) can have any step, context, or value block added to it, in sequence. * A parameter socket can only have the correct type of block inserted. * A filled parameter block *may* allow a matching block to replace the existing block, but only with a prompt. * A filled parameter block that is replaced with a block which takes a matching argument may auto-wrap the replaced argument. * A block which does not match but has a conversion block *may* prompt to ask if you would like to convert it to the needed type. * (Nesting) I *may* allow arithmetic and conversion blocks to be nested as an option. (Edit) Nope, instead there will be a way to collapse formulas into one virtual block and edit them as text. * Clicking a step or context in the block menu with a block selected will insert the clicked block after the selected block the same as dropping it. * If multiple blocks are selected and a context block in the block menu is clicked, the clicked block will wrap them * If a block is selected and you click a block in the menu while holding down Ctrl, then the new block replaces the old * Clicking a value block with the value cursor in a matching place will insert the block (if the space is filled or requires conversion, then prompt the same as for dropping) * Undo/redo never prompts, but copy/paste does. * There is only one cursor at a time, but it could be a block cursor OR an argument cursor. This eliminates ambiguity when you click a value block in the menu (or paste a value block). * If you click a block in the menu (or paste) and it cannot be inserted at the cursor, a notification will explain why unless turned off * When you copy blocks, a notification will tell you how many blocks are on the clipboard, unless turned off * When you drop a block in an illegal location, there is no notification why it failed (because of other feedback mechanisms) by default, but that can be turned on * When a value block is dropped into a parameter socket of a late-typed block, the other values and/or return type may change to match. Once changed, they do not revert to late-typed or untyped simply by removing the value block, although there may be a context menu or other mechanism to revert. Undo will revert it * You cannot drag (or functionally click) a late-typed value block (whether a returned block or a local) until it has had its type bound * Moving or removing a block can have serious side effects. Before the block is truly removed, warn the user how many blocks will go out of scope and be deleted or flagged as invalid (i.e., all of its locals and returned value that have been used elsewhere, and any other blocks that depend on those). * If a block is replaced by a block that can also fulfill it's responsibilities above (returned block is the same type, any locals used are the same types), then prompt to ask if it should be fully replaced instead of the warning above. * Should values blocks used as steps become Contexts within which they are in scope? Or should they be dragged to the locals of the appropriate context rather than placed as steps? What if their purpose it to give a return value? ## Waterbear Code Summary [Waterbear](http://waterbearlang.com/playground.html) is currently around 9095 lines of CSS, JS, and HTML (not counting examples or libraries). [Bloc](https://dethe.github.io/bloc/) is 568 lines of CSS, JS, and HTML (with better dragging behaviour and a very limited domain). What do I really need for Waterbear 2.0? What does Waterbear currently *do* with all of that code that Bloc doesn't do? There are some obvious things, but I want to do an apples to apples comparison. ### playground.html (1185 lines) This one is easy: All the blocks for the menu are in HTML, along with tags for the UI which is much more complex than Bloc's * Search * Disclosure menu * action buttons * Dialogs * Hidden SVG for text sizing (don't need) * Tags to load a LOT more libraries * Tags for customizing the layout * The embedded run area (as an iframe), so I really should count play.html too. ### app.css (326 lines) The goal for this file is to be about layout of the UI, but I think some clutter made it in. Interestingly enough, there is no dragging.css file, but functionality for it is scattered among several files. * sizing page, sidebar, header, menu, menu buttons * some styles for the debugger, which isn't in the codebase anymore... * a bunch of button styles for menu * the all-important sidebar trashcan, which I really do love * feedback area for messages, which I have no idea about (oh, it's the grey bar at the bottom of the page, gotta fix that) * The workspace, where blocks are assembled * A whole bunch of colours and icons for the accordion menu (easy to get out of sync with blocks and not layout related) * Fix to prevent selecting text while dragging (doesn't belong here) ### block.css (575 lines) * Shape and colour for custom blocks: wb-step, wb-context, wb-expression, wb-workspace, wb-value, wb-unit, wb-local, wb-contains, wb-disclosure, * cursor for blocks when not dragging * More helpers for dragging * The nice tab on steps * The much harder slot on steps and containers, which is background-white, not actually carved out. Getting the layering right for this was HARD. * I guess I should also count the 9 lines of SVG for the tab. * A bunch of tweaks for margins and padding * Code for highlighting selected block(s) * More dragging styles and fixes * More support for the debugger * A general definition for `.hide` * Icons for the value sockets (each of which is SVG, so more lines of code I didn't count) * The SVGs are aligned with each other, so each one has custom positioning. Screw that, position once and fix the graphics to match. * Icons for the expression blocks too * Fixes to prevent blocks from acting like blocks when they are in the menu * CSS for disclosure implementation (in blocks and accordion menu, I think)(Apparently the native disclosure list type is only implemented in Firefox, but the Details and Summary elements are implemented across the evergreen browsers natively). * Code to hide the secret SVG for text sizing * Hide wb-icons (why?) ### block_colors.css (98 lines) Newly refactored to use CSS variables. Mostly does what it says on the tin: provides colours for blocks, based on the block's namespace attribute. * Handles dimming element while dragging * Highlights drop-targets while dragging * Highlights selected blocks There is room for further simplification ### widget.css (263 lines) Support for non-block custom elements used to construct the GUI. While app.css defines the layout, widgets.css is more about functionality. * Defines wb-search and wb-accordion as block elements * Uses flexbox to implemeent wb-hbox and wb-vbox elements for layout helpers * Also wb-leftbox and wb-rightbox for some reason? * I don't even know what wb-workbox and wb-displaybox are supposed to be * wb-splitter for resizing hboxes and vboxes. * Inner rules for wb-workspace, which also uses flexbox * Debugger support made it here as well * Each of these files has a fair amount of commented out code * Size the canvas * Give wb-search a hard-coded height * Accordion internal layout and functionality * Sidebar filtering for search * I don't know what the `.cat-icon` is * Lots of accordion code. If all of that can be replaced with details/summary I should be able to remove a lot of code. ### app.js (262 lines) All (I think) of the JS files are implemented as IIFEs and could be reimplemented as JS modules. * implements `message()`, `error()`, `warn()`, `tip()` and `info()` in a way that is not readily localizable and where each message overwrites the previous one. Use a freaking library for this. I don't even know if we use this functionality anywhere, but if we do, use a library. * `clearFilter()`, `setFilter()` implement "search" (which is really just a filter) * Modals implemmented with NanoModal.js, can maybe move to native HTML modals now? No, apparently it is behind a flag in Firefox and not implemented in Safari. Ugh. * Event handlers for `.do-run` and `.do-stop` buttons wired up * `startScript()`, `stopScript()`, and `stopAndClearScripts()` implemented * `preload()` implemented * `runScript()` implemented * `handleFileButton()` shows a menu of buttons in a modal, and all of the buttons are implemnted in JavaScript rather than HTML * Wire up the file button event listener * `handleExample()` and `handleExampleButton` implemented and wired to the examples button. Like files and other "menu" modals the contents of the modal (more buttons) are defined here in JavaScript rather than in HTML. * We're also doing Google Analytics tracking for the buttons. * Add dragging handlers to listeners for both mouse and touch events. These may be for block dragging, but may be for dragging support in Waterbear scripts. * Add keyup/keydown event handlers * Wire up undo/redo to buttons and hotkeys * Wire up `Event.clearKeysOnFocusChange` to input blur / input focus events. Not sure what that does. * deselect all of the blocks and unfilter the sidebar if the 'Available Blocks' button is clicked * Clear the undo/redo stacks initially * Export the message and filter functions on the App object. ### app_play.js (101 lines) This is the version of app.js included in play.html and has versions (maybe just cut-and-paste duplicates, I haven't checked) of many of the functions in app.js, so I'll only call out what is new (there is probably a lot missing too, again, I haven't checked beyond the line count). * `setBreakoutURL()` seems to be a way of escaping from an embedded script (play.html) to the full IDE of playground.html. While this script does define and wire up `startScript()`, `runScript()`, and `stopScript()`, as well as the messaging functions, only the messaging functions are exported from the IIFE. ### block.js (1406 lines) OK, this is the meat of the block language of Waterbear, but also a place where a lot of simplification can take place. It defines the custom elements used for blocks, but these include a lot of functionality for building and running scripts directly from blocks that was abandoned / radically simplified in Bloc and I don't think needs to be brought back. Specifically, by avoiding this, I think it will be easier to incorporate Moonshine AND bring Waterbear back to being a toolkit for block languages (via Tardigrade block toolkit: Waterbear is a specific combination of IDE, blocks, and the Moonshine language). There is also a lot of code for implied elements, which I have mixed feelings about The "class" hierarchy is kind of messed up, in part to simplify the CSS, and uses/abuses prototype inheritance to route around inheritance problems, which if I really need to do should probably just use composition exclusivly rather than this mishmash. * Brings in a namespaced script global (dom.html) as (non-namespaced) local with shorter name (elem) for convenience. * Shortcuts for commonly-accessed elements of the IDE, which seems like a bad idea. * `setTypeOfVariable()` which notes that it should be a method of Step. When a block can accept / return multiple types of value, this updates it to a specific type when that type is added (concretizes the block value type). I think that is the purpose, anyway. Again, something to avoid by not mixing types. * `updateLocalInstancesType()` similarly seems to be doing something that should not be done, since changing the type signature can change the return type, and invalidate existing variables. Why would you ever want to do that? Ugh past me, that's dumb. * `handleInputOnBalance()` which seems to be trying to ensure after the fact that a number value fits between the specified min/max of its owning number socket, which isn't a terrible idea, but the function is poorly named and you shouldn't be allowed to put a mismatched value block into a socket in the first place (although I suppose you could put it in, then update the value?) * `updateVariableNameInInstances()` propagates changes to a named variable (i.e., automatic refactoring) which seems like a good idea. * grabbing a handle to the secret SVG text area with a note that it should be inserted by code rather than included in the HTML, which is probably a good idea. * `resize()` uses the secret SVG text area to resize text inputs as needed, since they don't seem to have an option to autogrow? * `setDefaultByTag()` is used to add implied element if they are not explicitly added, mixed feelings about this, and if I am generating blocks from Moonshine parse tree objects, then it shouldn't be needed? The heresy (or whatever) template should create all the structure needed, right? * Hmm, the SVG for tabMarkup and tabPath are both included as JS variables (and not parsed from tab.svg). I have a bad feeling about this. * `setDefaultTab()` adds the tab if it is not present. Is this to work around limits on where the CSS was able to add the tab? This is newish code, but I have only vague understanding of it. * `setDefaultSlot()` does something similar for the cutout tabs should fit in. Both have hardcoded integer values and use tabPath but not tabMarkup. I don't understand. We're coming up on the first custom tag, albeit one that is never instantiated: BlockProto! Only used via inheritance by other tags and can be eliminated to be replaced by functions for composition. * but first, an array called `headerOnly` which lists wb-row, wb-value, wb-disclosure, and wb-local. I'm sure this will make sense later. * `BlockProto.createdCallback()` (whose very existence does not make sense) uses headerOnly to determine if children should be added after the header or not. If I have composable functions I should be able to just use different one for the different blocks. Otherwise, this is setting up internal structure which will be the duty of a templqte. Also, every block is given a random id, which is used to find their instances for renaming later, I think. * `BlockProto.attachedCallback()` comes with extensive notes. Because attachedCallback is only called once (the first time a block is added to the DOM), I apparently synthesized wb-added, wb-addedChild, and wb-removedChild events (and attempted to synthesize wb-removed but there was nothing to send the event to, LOL). Also, this will fire when loading a saved script. The main duties it has are to add locals (elements) and make sure they have names that are unique in scope (no shadowing). Because iteration is overloaded, locals for loops cannot be added until the loop knows what it is iterating over, which is broken as fuck and needs to be fixed (possibly by never lettting me code again). I'm not even sure I understand the comments, but I think the special cases are for handling when a block that inherits from this is added to certain locations. * `BlockProto.header()` is nice and simple, just return a reference to the &lt;header&gt; element. * `BlockProto.gatherArguments()` is for running code directly from blocks: pulling values from argument blocks for running the code. I hopw we can get away without this by executing Moonshine instead. * `BlockProto.firstExpression()` returns a reference to the first expression block. * `BlockProto.localOrSelf()` returns the local block this is an instance of, if it is an instance, otherwise returns this block. * `BlockProto.gatherValues()` if this block has a value attribute, return the value of it (as an array of one item), otherwise find all arguments and return an array of their arguments. * `BlockProto.run()` either return this block's _currentValue (if defined) or if this block has a function name, get it's namespaced function and apply it to any block arguments UNLESS this block is marked as a `specialform` in which case we call the function without evaluating arguments first. * `BlockProto.hasLocal()` returns true if this block has locals, false otherwise. * `BlockProto.getLocals()` returns an empty list, intention is this will be overridden in subclasses. * `BlockProto.getFreeInstances()` get contained instances so we can get the local value from them. * `BlockProto.getInstances()` get all instances of the block's locals, I think. * `BlockProto.getLocalInstances()` get instances of a specific local, note says it is typically called on the parent context. * `BlockProto.undoableInsert()` is part of the overcomplicated undo/redo mechanism that I want to replace with immutable data structures. * `BlockProto.undoableRemove()` ibid * `BlockProto.removeInstances()` notes say this is for "validation maintenance" and "called when the block they are instances of is removed" which makes sense. Can't have instances of a non-existent block. An attempt is made to make this undo-able. * `BlockProto.removeOutOfScopeInstances()` similar to the above, but specifically looks to see if instances are now out of scope after a block is moved. Some care is taken to make this undo-able. * `BlockProto.getAncestorContexts()` looks like it does what it says on the tin, but I don't know why you need it. * `BlockProto.isInstance()` returns true if this is an instance, false otherwise. * `BlockProto.getLocalForInstance()` this seems really similar to other methods above. * `BlockProto.next()` this is a method specific to wb-step and wb-context and an argument for composition again. * There is a `BlockProto.isContext` property set to false, intended to be overridden by ContextProto. Not sure why this is a property instead of a method like the other boolean flags. Hey, we've gotten to the first real (instantiable) block: wb-step, aka StepProto! * `StepProto.createdCallback()` In a nod to composition, this calls it's super() and then calls setDefaultTab() and setDefaultSocket() * `StepProto.getLocals()` much simpler for steps than for contexts. * `StepProto.getDescendantLocals` is simply an alias for StepProto.getLocals. * Finally, we register `window.WBStep` as the prototype for `wb-step` elements and we're done! Moving on to the meatier wb-context. * `ContextProto.createdCallback()` does a bit more than StepProto does here, but not a lot, and nothing that can't be handled by the template. * `ContextProto.childContainers()` is a simple accessor. It does not traverse all descendants, only children. * `ContextProto.gatherContains()` This is a specialized version of getSteps() which gathers (as an array of arrays, I think) all child steps and contexts. it is intended for contexts which have multiple wb-contains children, of which I believe If/Else is currently the only one. * `ContextProto.gatherSteps()` returns an array of contained blocks (including both contexts and steps, despite the name). * `ContextProto.getLocals()` Doesn't get locals from decendent or ancestor contexts, use getDecendentLocals() and getAllContextLocals for those. * `ContextProto.getDescendantLocals()` A seemingly over-complicated way to return all decendant locals (there is a comment suggesting a simplification that has not been tested). * `ContextProto.getAllContextLocals()` get locals from both ancestors and descendants but not sibling contexts along ancestor axis And now for wb-workspace (which is more layout than block and probably should be in the IDE kit). * `WorkspaceProto.getLocals` an alias for ContextProto.getLocals and another argument for composition over inheritance. * `WorkspaceProto.getDescendantLocals` also an alias from ContextProto * `WorkspaceProto.getAllContextLocals` is *also* an alias for ContextProto.getDescendantLocals, because wb-workspace is always at the top of the context chain. * `WorkspaceProto.gatherContains` another alias from ContextProto. * `WorkspaceProto.childContainers` another alias from Context Proto. And that's it. WorkspaceProto is a ContextProto minus some bits, no template (it inherits from HTMLElement, not from BlockProto), and different CSS. Now to explore wb-expression. Context and Step blocks have a mandatory `script` attribute, but Expression blocks also have a mandatory `type` attribute. * `ExpressionProto.attachedCallback()` Checks that it has been inserted into a legal part of the DOM and hides any default argument block. * `ExpressionProto.detachedCallback()` unhides any default argument block(s) on the way out. * `ExpressionProto.removeInstances()` a do-nothing method, returns an empty list. Expressions don't have instances, although they can *be* instances. * `ExpressionProto.setValue()` If this is an instance, set the value for the local it is an instance of, otherwise set the value of the expression. * `ExpressionProto.getValue()` If an instance, get the value from the local it is an instance of. If there is a cached value, return it. Otherwise, get the value, cache it, and return it. Premature optimization? Now some functions for managing local variables (which currently only exist for expressions). * `updateVariableType()` An event handler specific to the `setVariable` block type. * `createLocalToInstanceAssociation()` Make sure an instace is marked and tagged with it's local's id so it can be mapped back. * `handleVariableInput()` An event handler specific to `getVariable` block type, I think it is just to update the names of instances when the name of the variable is changed. * `handleVariableBlur()` a cleanup event handler, when a variable is named, ensure it is unique (in scope) and not shadowing another variable. * `trailingNumber()` a utility for making names unique by adding a number, similar to how the Mac Filesystem works. * `ensureNameIsUniqueInContext()` "in context" should be "in scope". Also checks in descendant scopes, because we don't want to change to a name that now forces an existing variable to shadow us. This shit is complicated. * `incrementName()` Another helper function, uses trailingNumber() to get any existing numbering, then increments it (and has to be tested again to make sure that name isn't taken). * `uniquifyVariableNames()` an event handler to ensure names are unique by calling the above ensureNameIsUniqueInContext(). And now we get to wb-unit, whose scope may expand dramatically in the next version, since I'd like to be much closer to Frink on this topic. Currently, it inherits from HTMLElement and adds no functionality beyond being a unique element for purposes of CSS. Next up, wb-row, an element I would like to eliminate, if possible, as it adds a lot of complexity. * `RowProto.getValue()` defers to any contained wb-value blocks, always returns an array, which may be empty. OK, maybe not so much complexity here, but it makes templating the steps which use it harder. Now for wb-disclosure, which will definitely go away. * `toggleClosed()` adds or removes the `closed` attribute, CSS does the rest. Now wb-locals are interesting. Local holds a single expression block, acting as a tiny blockmenu of one, inline to another block. They inherit from HTMLElement, so overall they don't behave like blocks themselves. * `LocalProto.getValue()` defers to its contained expression block. Back to functions. * `addItem` event handler for the Add Item button. Clones a row and its children, but not their contents. Used to add more items to an array, for instance. This will move to the Array Editor form. * `removeItem()` The sister event hander to addItem(). Remove the item, unless it is the last item (in which case it *shouldn't* have a removeItem button, but good to check in case someone clicks very fast). Next block is wb-value, derived from HTMLElement. This has a mandatory type attribute, but also supports attributes value, min, max, and allow. Allow can be literal (can only type in literals, not drag in blocks), block (can only drag in blocks, not type in literals), or unset (either is allowed). It can contain input, select, or wb-expression as children. Type can have multiple values, comma-separated, which I want to do away with. * `ValueProto.createdCallback()` Does some complex checking. May need to build up a template, or may be cloned from an existing block and have everything in place already. Uses different inputs depending on the type, (some of which need to be customized further). Resizes if necessary. * `ValueProto.getValue()` Can return various things depending on type. Really need to eliminate multiple types and type propagation. And more functions * `toggleFilter()` this is an event handler which auto-filters when an input is focused, to only show blocks which are valid to target that input. * `changeValueOnInputChange()` Event handler to update a block's value. There is also a simple map of conversion functions for returning booleans (true only if text is the string "true", numbers, and "any" (number if conversion works, otherwise text). The wb-contains block inherits from HTMLElement, but is block-ish in some contexts. It can only exist in a wb-context (or wb-workspace) and every wb-context must have at least one. * `firstInstruction` a getter property to return the first element child, which will be either a wb-step or wb-context block. If I were to continue with this type of code it would benefit to use getter properties more and to use the Class syntax that JS has now. Man, I've been working on this for a long time. * `ContainsProto.getAllContextLocals()` gets locals from parent, whether it is a wb-context and wb-workspace. * `ContainsProto.getFreeInstances` is an alias for BlockProto.getFreeInstances. Yadda yadda composition over inheritance yadda. And now I have, somewhat inexplicably, a whole bunch of dragging code, including file-local globals: dragTarget, dragStart, dropTarget, blockTop, undoDrag; I had assumed there would be a dragging.js module, but apparently it is all part of block.js. No wonder this file is so big! dragStart is either 'script' or 'menu'. If 'menu' we are actually dragging a clone, delete it on cancel. If 'script', we are dragging the original, remove it from its parent, but put it back on cancel. Replace all this crap with dragula. * `startDragBlock()` an event handler. Won't start dragging if the Ctrl key is pressed. Tries to implement undo/redo. There is also some app-level layout stuff, like showing the trash can while dragging. Apparently highlighting valid drop targets is not implemented yet. * `checkForScroll()` This is another event handler, but appears to add itself to requestAnimationFrame in an endless cycle once called, that may be a bug. This allows us to scroll longer scripts as we drag to the bottom or top. * `markDropTarget()` Adds classes to allow drop targets to be highlighted, so maybe that is implemented, but the comment wasn't deleted? * `canInsertHere()` Given a block and a target, return true if the target is valid for that block. Actually returns an object with {test: true|false, reason: "..."}. Mostly defers to canInsertExpressionHere() and canInsertStepHere(). * `insertHere()` inserts a block into a target. Takes an optional event when dragging. Complex because it has to work for multiple block types. Should probably defer to methods on the individual blocks. * `canInsertExpressionHere()` Needs work to check for valid scope. Prevents inserting into a literal and handles weirdness about inserting variables that I don't completely understand. Prevents dropping an expression in a socket that already holds an expression, which I might want to be smarter / more helpful about if types match up. Ensures types match (but doesn't check min/max). Attempts to give helpful information to user while dragging around. * `canInsertStepHere()` Has a note to fix so you cannot drop onto locals. There is weirdness about where a new block will end up in a list which should be a LOT better when I switch to using Dragula instead of my half-assed code. * `dragBlock()` Event handler. Really need to use Dragula instead. * `isInsertionInScope()` Helper to check scopes after insertion. Especially with cut/paste things can go radically wrong. * `addToContains()` Helper for dropping a step. * `endDragBlock()` Event handler for dragging, handle unexpected cases. * `cancelDragBlock()` Handle cancel. * `resizeInputOnChange()` Event handler for input changes, still somewhat buggy IIRC. * `resetDragging()` Clear all dragging states. * `createVariableBlock()` I *think* this gets created when we drop an expression in where a step should go, so it creates a step to hold the expression and a local to reference it. Then there are nearly 30 lines of registering event listeners. The IIFE exports a Block object with which exposes BlockProto, valueProto, containedProto, canInsertHere, and insertHere. I am afraid to find out why (I think the last two are for copy/paste). ### cutandpaste.js (205 lines) Starts off with a useful comment, that this module depends on select.js, block.js, event.js (maybe others?). If I were using real js modules, that would be obvious, but since I'm not, it is helpful to see. Also notes of some limits/bugs: // FIXME: Manage scope // FIXME: Manage cut (vs. copy) Then we set up a bunch of file-local globals: sekritSelection, sekritValue, parseBlock, defaultSelection, and pasteboardCancelled. The way I worked around lack of support for the genuine pasteboard API in the browser is that whenever an element is selected, I copy the outerHTML (with some discretion and special-casing) into a hidden textarea. Then, if the Ctrl or Meta key is pressed, I select the text in the textarea. That way, the native copy/paste will get the right content. If the contents of the selection have changed, then I assume a paste happened and try to insert the HTML into the document. Some of this should get easier when I have a real text representation and a parse tree. Some will still be a PITA (i.e., scoping). Still, it works OK-ish and is a pretty resilient hack. * `cancelPasteboard()` some other event, like undo/redo has the command key, just set the pasteboardCancelled variable to true for now. * `setupPasteboard()` called when the selection changes. Warns if there are no blocks selected, or more than one (we can currently only copy one outer block, plus all of its contents). If a value is selected, copy its inner expression, if any. * `setupForCopyPaste()` Called when the Ctrl / Meta key is pressed. Check for failure conditions, otherwise select the contents of the secret textarea. * `cleanupFromCopyPaste()` Make sure Ctrl or Meta are part of the event being handled. If the pasteboard event was cancelled (variable is set) bail out now. If the default text is selected, bail out now. Otherwise collapse the selection. If the selection was cleared, assume a Cut and delete the existing selection from the DOM by calling handleCut(). If it is a paste, replace the selection with the pasted HTML by calling handlePaste(). We don't have to handle Copy because the code is now in the OS pasteboard. * `handleCut()` First, check to see if they are valid to paste into the script (parseable, valid blocks). Grab ids from the parsed HTML, then delete the parsedHTML (with duplicate ids). Find the element(s) with those ids and delete them. Push all changes onto the Undo stack. * `handlePaste()` Check to see if they are valid to paste into the script (parseable, valid blocks). Bail if it is not HTML content. Clone the parsed HTML (using my clone utility that provides new IDs). Hmm, this will probably screw up if there are instances referenced by ID, I don't see code to fix that. If current selection can be replaced by the new blocks (and we have confirmed that they are in fact blocks), do so. Try to give user feedback about what is happening, especially if it doesn't work. * `confirmCutCopyPaste()` Event utility to convert Ctrl/Meta + c | v | x into wb-copy, wb-paste, wb-cut custom events. * `doCut()`, `doCopy()`, `doPaste()`, `doClear()` are all do-nothing functions that log "implement me" to the console. I'm not sure why. At the end of the file, we register event handlers for both the working system and the unimplemented one. Then we export a global Pasteboard object with one function: cancelPasteboard. ### dom.js (318 lines) I thought this was a stripped down version of my old dom utilities, but it's pretty big and still has a lot of code I don't think we use in this project. I'm pretty sure I did make some modifications to it, like changing IDs when cloning an element. It defines variables for the SVG namespace and the XLink namespace, and exports many functions: `html()`, `element()`, `svg()`, `setXLinkAttr()`, `setAttributes()`, `walk()`, `replaceID()`, `clone()`, `appendChildren()`, `elemToObject()`, `remove()`, `insertAfter()`, `closest()`, `find()`, `findAll()`, `child()`, `children()`, `parent()`, `matches()`, `addClass()`, `removeClass()`, `nextSibling()`, `prevSibing()`, `toggleClass()`, `indexOf()`, and`createSelect()`. I'm not going to go into detail about these except to say that several can now be replaced by build-in DOM seems genuinely useful methods and that the Clone-but-replace-Ids (but may not go far enough). The html() routine will probably be replaced by heresy templates. ### event.js (650 lines) This bills itself as a "bare-bones event library" but goes into some pretty detailed areas. I don't remember writing the core of this, so it may have been someone else's code that I extended. If so, I extended it quite a lot, adding support for event delegation on top of normal DOM event like JQuery "live" event handlers, minimal support for custom events, normalization between mouse and touch events, namespaced events (makes it easier to delete a whole whack of event listeners at once). I don't know, maybe I wrote the rest too. Some of it is obviously from my old dragging library. There is part of it that is for use with the drag-and-drop and key handling in the Waterbear GUI, but there is also code to be used by Waterbear scripts to query mouse position and state, key states, etc. It exports a LOT of functions and properties, but I'm not interested in it right now. Moving on. ### file.js (268 lines) For such a bundle of outrageous hacks, I'm fairly proud of this code. It lets me load and save scripts to/from LocalStorage, Gists, Example code (load only), and the Filesystem. It can be extended pretty easily and it can be adapted to other file types (as I did with Shimmy). I need to make sure that a) the hacks still work, and b) there aren't better ways to do some of these things now, but overall I quite like this file. ### queryparameters.js (51 lines) This code definitely came from stack overflow and should be in libs, not in js. A simple utility, ignoring for now. ### runtime.js (1571 blocks) This is even bigger (a little) than blocks.js and either implements most of the Waterbear languge or exposes bits of external libraries (SAT.js for collision detection, timbre.js for music/audio). Not terribly important for implementing the tardigrade-level block code I'm working on or the moss-piglet-level GUI, and for the actual runtime will probably be more-or-less replaced by Moonshine code. ### select.js (165 lines) This code handles selecting blocks and sockets. First we grab handles to the workspace and block menu DOM objects. * `handleEnter()` Pressing the Enter key deselects and clears the current selection, also clearing filters. * `Block.proto.makeSelected()` Interesting, adding new methods to BlockProto from another module. Unselects any previously selected blocks and adds the selected class to this one, the rest is CSS. * `Block.valueProto.makeSelected` adds the above method to ValueProto (composition). * `Block.containedProto.makeSelected` ibid, but for ContainedProto. * `isBlockSelectable()` Literals and locals are not. Nor are values which already contain a block, or values which don't have a socket. * `unselectAllBlocks()` Removes the selected class from any blocks that have it. * `manageSelections()` Event handler. If it is called on a block, selects that block, otherwise unselects all blocks. * `insertBlockAtSelection()` Click/Tap a block in the menu to insert it at selection (if possible. We clone because we assume this is coming from a block catalog. Attempts to communicate what is going on, especially on failure. * `getSelectedBlocks()` Returns both selected value and selected block. Having these as separate concepts is something I hope to fix in the next version. * `clearSelection()` Event handler, clears either the selected value OR the selected block, but (I think) not both. This module exports only one function: Select.blocks() (which is an alias for getSelectedBlocks()). After that, it adds eight event handlers. And for the grand finale, we have util.js, which is a hodgepodge of utility methods, implementations of the runtime, polyfills, and an entire multimethod implementation that I will probably remove from Moonshine, but could be useful in other JavaScript projects. It wouldn't even be that bad in Moonshine if I could figure out how to manage the various methods as separate blocks (or a block that you can choose the signature of? Nah.) ### util.js (1326 lines) Starts off by creating local names for various Math functions for convenience: cos, sin, atan2, sqrt, floor, and PI. Defines file-local globals for DEGREE, _lastPoint, and drawingPath. Having the last two as globals makes the vector drawing functions non-reentrant. Next is a polyfill for `Function.prototype.bind` because it isn't (or wasn't) supported in PhantomJS, which we briefly used for testing. --- Some vector drawing helpers. * `isDrawingPath()` returns the boolean global drawingPath value. * `lastPoint()` returns the global _lastPoint. * `setLastPoint()` sets the value of _lastPoint. --- Some actual utility functions! * `deleteItem()` Removes an item from an array, because JavaScript doesn't have a way to remove a specific item from an array. * `inList()` because JS didn't used to have a way to do this cleanly (comparing array.find() to -1 is not cleanly), but can now be replaced with `array.includes()` * `isNumber()` Checks to see if a string represents a valid number (also returns true for actual numbers). * `extend()` Looks like my own version of Object.assign(). --- Then some lines to remove namespaces from requestAnimationFrame and cancelAnimationFrame, neither of which should be needed now. * `setDefault()` Pulling in a useful object function from Python. * `type()` A vastly improved (i.e., actually useful) version of typeof. --- Implementation of multimethods * `Method()` Defining the Multimethods object * `Method.prototype.when()` Add a method, dispatched by all types on the method. * `Method.prototype.tryInverse()` If the order of arguments is not important, this can be used to avoid having to define the method twice. * `Method.prototype.default()` What to do when no match is found * `Method.prototype.send()` Make a dispatched call to the multimethod. * `Method.prototype.fn()` Wrapper for send() --- Implementation of vectors. Math deferred to multimethods. * `ded2rad()` Tiny util * `rad2deg()` Ibid * `mod()` Replacement for JavaScript % operator because of sign conversion. Yeah, I don't know what that means either. * `angle()` Angle between two vectors, in radians * `dist()` Distance between two points * `Vector()` Constructor for a vector from x,y * `Vector.fromPolar()` Create a Vector from angle in degrees and magnitude * `Vector.fromPoin()` Create a Vector from a point (or another Vector) * `Vector.prototype.getX()` Yeah, I should really use the Class notation and accessors, but they didn't really exist then. * `Vector.prototype.getY()` Ibid * `Vector.prototype.magnitude()` Calculates this each time, should probably cache it since Vectors are intended to be immutable. * `Vector.prototype.radians()` Ibid * `Vector.prototype.degrees()` Ibid * `Vector.prototype.normalize()` Returns vector if magnitude is already 1, otherwise returns a new Vector of magnitude 1. * `Vector.prototype.rotateTo()` Returns a new vector rotated to a specific degree * `Vector.prototype.rotate()` Returns a new vector rotated relatively by degrees * `Vector.prototype.rotateRads()` Relative rotation by radians * `Vector.prototype.toString()` Returns an <x,y> representation that I may use in Moonshine. --- Define the Size type * `Size()` takes a width, width unit, height, and height unit. Moonshine should combine numbers and units into specific types and use those consistently throughout. I don't need all the types that Frink supports right away, but at least most of the ones CSS supports. * `Size.prototype.toString()` Throws away the units. Why? --- Define the Rect type * `Rect()` takes x,y, width, height but no units * `Rect.prototype.getPosition()` Returns a Vector from x,y * `Rect.prototype.getSize()` returns a Size from width/height in pixels * `Rect.fromVectors()` Treats two vectors or points as x,y and width, height --- Define the Path type * `Path()` takes a function to call, an array of input points, and a start point. * `Path.prototype.draw()` This seems weirdly complicated and I'm not sure how the function is used. Did one of my students write this? --- Define the Shape type * `Shape()` Takes either a path, an array, a function, or an SAT object and a points array. All of those should be separate constructors! * `Shape.prototype.draw()` Is predictably complicated, since each of the above is just stored and then has to be applied here. --- Multimethods for math Add * `add = new Method()` * `add(array, number)` * `add(array, vector)` * `add(array, array)` * `add(vector, number)` * `add(vector, vector)` * `add(number, number)` * `add.tryInverse()` Subtract * `subtract = new Method()` * `subtract(array, number)` * `subtract(array, vector)` * `subtract(array, array)` * `subtract(vector, number)` * `subtract(vector, vector)` * `subtract(number, number)` * `subtract(date, date)` * `subtract.tryInverse()` Multiply * `multiply = new Method()` * `multiply(array, number)` * `multiply(array, vector)` * `multiply(vector, number)` * `multiply(number, vector)` * `multiply(vector, vector)` Dot product * `multiply(number, number)` Divide * `divide = new Method()` * `divide(array, number)` * `divide(vector, number)` * `divide(number, number)` Equal * `equal = new Method()` * `equal(date, date)` * `equal.default()` NotEqual * `notEqual = new Method()` * `notEqual(date, date)` * `notEqual.default()` --- Randomness * `randInt()` * `fade()` * `lerp()` * `grad()` * `scale()` * `permutations = [long array]` * `noise()` Ken Perlin's noise function * `choice()` Choose one from an array --- Motion Motion here refers to the accelerometer and converts deviceorientation events into motionchanged events and a readable variable `direction`. It is a terrible API, quite useless, mixing left/right tilt and front/back tilt into one coarse-grained description. Bleargh. * motionModule is the result of an IIFE that returns an object. * internally it case function-global variables `direction` and `motionModule` WTF? * `motionModule.startTrackingMotion()` * `motionModule.direction` getter function * `onMotionChange()` internal event handler --- Geolocation mini-module This module is done in the same odd style as the motionModule, I think the same student did them both. Starts fetching the location, periodically, and sets the `currentLocation` property of this module. Use `geolocation.isTracking` to check whether geolocation is ready. In addition to the read-only properties and methods on the geolocationModule itself, it also sends a `locationchanged` event periodically. --- Assets `assets` is an module that ensures that sounds, images, and other things are loaded before the start of the script.`assets.load` works by determining whether the asset is needed by matching a selector on the document (usually this means elements inside the script). Once elements are matched, they're passed to a given callback that should set in place the asynchronous loading of said elements. Once all elements are done loading, the callback provided with the selector is called. This whole thing is a) a lot of magic (pre-loads named media even if not told to) and b) better handled by a library that specializes in such things. Also, c) like much of this file, it belongs in the Moonshine runtime, not the Tardigrade IDE code. Ultimately it should expose two blocks, something like preload(assetList) and when(asset) is loaded then (code) else (error). --- Image, loadable, drawable One of th concepts or conceits of Waterbear is that Images should be something you can draw on, so if you load an image and call drawing operations on it, we would automatically convert it to a canvas, and if you just want to draw, we would create a canvas as an "empty" image. Whether we actually use Canvas or SVG is another topic altogether. * `WbImage()` Create a WBImage with a given image src and callback. * `WBImage.create()` Create an empty WBImage with the given width and height. * `WBImage.prototype.draw()` Draw the current image into the given drawing context. * `WBImage.prototype.getContext()` return the wrapped drawing context. * `WBImage.prototype.drawAtPoint()` Draw into the context somewhere besides (0,0). * `WBImage.prototype.drawInRect()` Draw at a postion and size. * `WBImage.prototype.getHeight()` Ought to be a getter * `WBImage.prototype.getWidth()` Ibid * `WBImage.prototype.setWidth()` Ack, a mutator! Kill it! Kill it with fire! * `WBImage.prototype.setHeight()` Ibid * `WBImage.prototype.setSize()` Ibid * `WBImage.prototype.scale()` Thse would be OK if they returned a new WBImage, maybe even it were backed by the same <img>, unless you can change the src. * `WBImage.prototype.flipH()` Ibid * `WBImage.prototype.flipV()` Ibid * `WBImage.prototype.flipBoth()` Ibid * `WBImage.prototype.toString()` Should make sure this can be parsed by Moonshine. --- Sprite mini-library The Sprite constructor takes a Drawable, which can be a shape function, an image, or text * `Sprite()` * `Sprite.prototype.accelerate()` * `Sprite.prototype.setVelocity()` Are sprites a usecase where it makes sense to let them be mutable? Not really, especially if we have structural sharing. * `Sprite.prototype.getXvel()` * `Sprite.prototype.getYvel()` * `Sprite.prototype.getXpos()` * `Sprite.prototype.getYpos()` * `Sprite.prototype.applyForce)_` Applies a vector as a force for the Sprite's momentum (velocity) * `Sprite.prototype.rotate()` Relative rotation * `Sprite.prototype.rotateTo()` Absolute rotation * `Sprite.prototype.move()` Apply sprite's velocity to it's position * `Sprite.prototype.moveTo()` Just put the Sprite somewhere * `Sprite.prototype.draw()` Draw to the provided context * `Sprite.prototype.angle()` Getter for the rotation value * `Sprite.prototype.toString()` Make this parseable by Moonshine * `Sprite.prototype.bounceWithinRect()` Like Scratch's bounc(), but generalized to any (non-rotated) rect. * `Sprite.prototype.checkForCollision()` Uses SAT for collision shapes and testing * `Sprite.prototype.wrapAroundRect()` Wrap-around in the Space Wars sense, not the giftwrap sense. * `Sprite.prototype.stayWithinRect()` Trapping, instead of bouncing. --- Default Drawable (???) * `defaultDrawable()` I have no idea what the purpose of this is. --- And back to utility * `randomId()` A useful little one-liner based on Paul Irish's random colour tool. Maybe not the best for unique IDs though. Aaaand that's all, folks!

Import from clipboard

Paste your markdown or webpage here...

Advanced permission required

Your current role can only read. Ask the system administrator to acquire write and comment permission.

This team is disabled

Sorry, this team is disabled. You can't edit this note.

This note is locked

Sorry, only owner can edit this note.

Reach the limit

Sorry, you've reached the max length this note can be.
Please reduce the content or divide it to more notes, thank you!

Import from Gist

Import from Snippet

or

Export to Snippet

Are you sure?

Do you really want to delete this note?
All users will lose their connection.

Create a note from template

Create a note from template

Oops...
This template has been removed or transferred.
Upgrade
All
  • All
  • Team
No template.

Create a template

Upgrade

Delete template

Do you really want to delete this template?
Turn this template into a regular note and keep its content, versions, and comments.

This page need refresh

You have an incompatible client version.
Refresh to update.
New version available!
See releases notes here
Refresh to enjoy new features.
Your user state has changed.
Refresh to load new user state.

Sign in

Forgot password

or

By clicking below, you agree to our terms of service.

Sign in via Facebook Sign in via Twitter Sign in via GitHub Sign in via Dropbox Sign in with Wallet
Wallet ( )
Connect another wallet

New to HackMD? Sign up

Help

  • English
  • 中文
  • Français
  • Deutsch
  • 日本語
  • Español
  • Català
  • Ελληνικά
  • Português
  • italiano
  • Türkçe
  • Русский
  • Nederlands
  • hrvatski jezik
  • język polski
  • Українська
  • हिन्दी
  • svenska
  • Esperanto
  • dansk

Documents

Help & Tutorial

How to use Book mode

Slide Example

API Docs

Edit in VSCode

Install browser extension

Contacts

Feedback

Discord

Send us email

Resources

Releases

Pricing

Blog

Policy

Terms

Privacy

Cheatsheet

Syntax Example Reference
# Header Header 基本排版
- Unordered List
  • Unordered List
1. Ordered List
  1. Ordered List
- [ ] Todo List
  • Todo List
> Blockquote
Blockquote
**Bold font** Bold font
*Italics font* Italics font
~~Strikethrough~~ Strikethrough
19^th^ 19th
H~2~O H2O
++Inserted text++ Inserted text
==Marked text== Marked text
[link text](https:// "title") Link
![image alt](https:// "title") Image
`Code` Code 在筆記中貼入程式碼
```javascript
var i = 0;
```
var i = 0;
:smile: :smile: Emoji list
{%youtube youtube_id %} Externals
$L^aT_eX$ LaTeX
:::info
This is a alert area.
:::

This is a alert area.

Versions and GitHub Sync
Get Full History Access

  • Edit version name
  • Delete

revision author avatar     named on  

More Less

Note content is identical to the latest version.
Compare
    Choose a version
    No search result
    Version not found
Sign in to link this note to GitHub
Learn more
This note is not linked with GitHub
 

Feedback

Submission failed, please try again

Thanks for your support.

On a scale of 0-10, how likely is it that you would recommend HackMD to your friends, family or business associates?

Please give us some advice and help us improve HackMD.

 

Thanks for your feedback

Remove version name

Do you want to remove this version name and description?

Transfer ownership

Transfer to
    Warning: is a public team. If you transfer note to this team, everyone on the web can find and read this note.

      Link with GitHub

      Please authorize HackMD on GitHub
      • Please sign in to GitHub and install the HackMD app on your GitHub repo.
      • HackMD links with GitHub through a GitHub App. You can choose which repo to install our App.
      Learn more  Sign in to GitHub

      Push the note to GitHub Push to GitHub Pull a file from GitHub

        Authorize again
       

      Choose which file to push to

      Select repo
      Refresh Authorize more repos
      Select branch
      Select file
      Select branch
      Choose version(s) to push
      • Save a new version and push
      • Choose from existing versions
      Include title and tags
      Available push count

      Pull from GitHub

       
      File from GitHub
      File from HackMD

      GitHub Link Settings

      File linked

      Linked by
      File path
      Last synced branch
      Available push count

      Danger Zone

      Unlink
      You will no longer receive notification when GitHub file changes after unlink.

      Syncing

      Push failed

      Push successfully