Documentation
System
The system library is composed of functionality in several packages.
app
application scaffoldingaws
interface to AWS services (S3, Cognito, etc.)fs
file systempkg
building packagesui
user interface elements.util
utility functions
launch
system.launch (config) ->
alert "App launched!"
system.app.Base
loadFile: ->
newFile: ->
saveData: ->
Launches the application bootstrapping a postmaster instance, adding host and client properties.
app
app.Base
app.Base(app)
Construct a Base application with paste and drop events already wired up.
The app object must include implementations for the following methods:
loadFile(Blob, path)
Receive a blob and path and load it as the application state.saveData() -> Promise<Blob>
Return a promise that will be fulfilled with a blob of the representing the current application state.newFile()
Clear or reinitialize the application to an empty state.
The app object is extended with the following methods:
config
app.config
baseStyle : boolean
- whether to apply the base system stylesheet. Defaults to true.
# Opt out of default system stylesheet
app.config.baseStyle = false
confirmUnsaved
app.confirmUnsaved()
Returns a promise that resolves if there are no unsaved
changes or if there are unsaved changes then displays a modal that prompts the
user to confirmed losing unsaved changes. The promise is fulfilled if the modal
is confirmed, rejected otherwise.
currentPath
currentPath(string)
Observable representing the current path of the saved data. This is updated in
open
, new
, and saveAs
.
drop
drop(files)
A document drop event listener is automatically added that calls this method
app.drop
with e.dataTransfer.files
if present. The default implementation is
to call loadFile
on the first file passed in.
exit
exit()
Sends the exit message to the system host application proxy.
extend
extend(object)
Add additional methods to app. Same as Object.assign(app, object)
.
hotkey
hotkey(key, method: string|function)
Bind a hotkey using Mousetrap to a given
function or named function on app
.
The function is called with app
as this
. If the function is a string then
the property of app with that name is invoked as a function. In both cases the
first argument is the event. e.preventDefault()
will automatically be called
before invoking the function for all hotkeys that trigger.
new
new()
Displays a modal prompting the user to confirm unsaved if there are any unsaved
changes then calls newFile
to set the app to an empty state if confirmed.
open
open()
Displays a modal prompting to confirm unsaved then opens a modal prompting for the path to open.
paste
paste(files)
A document paste event listener is automatically added that calls this method
app.paste
with e.dataTransfer.files
if present. The default implementation is
to call loadFile
on the first file passed in.
save
save()
Gets the current state from saveData
then writes blob to the
host system at the current path.
saveAs
saveAs()
Displays a modal prompting for the path to save to then saves to that path if confirmed.
saved
saved(boolean)
Observable representing whether the application state is saved or not.
events
boot
dispose
aws
AWS provides an api for the server side of whimsy.space.
Cognito
handles all the user credentials for AWS users. Each whimsy.space user
has a cognito user. It also handles anonymous credentials. Users can sign up
and authenticate. After they have authenticated the keys are cached in the
browser's local storage. logout
will delete the cached keys
from local storage.
Cognito.authenticate
Cognito.authenticate(username, password) -> Promise
Authenticates a user by usernaem and password on AWS, caches credentials in local storage.
Cognito.cachedUser
Cognito.cachedUser() -> Promise
Refreshes the AWS credentials based on cached user token from local storage.
Cognito.logout
Cognito.logout() -> void
Deletes the cached local storage credentials so the user will no longer be logged in when they next visit the page.
Cognito.signUp
Cognito.signup(username, password, attributes)
api
api(path, params)
Execute an api request using the current aws credentials.
cdn
cdn(blob) -> Promise<urlSafeSha>
Uploads a file to the shared CDN using the current credentials. It returns a promise that resolves with a url-safe sha of the uploaded object.
fs
fs
contains all the file systems as well as utility functions for files and
paths.
Dexie
Mount
S3
absolutizePath
baseDirectory
baseName
extensionFor
isRelativePath
normalizePath
separator
textMediaType
withoutAllExtensions
withoutExtension
absolutizePath
absolutizePath(basePath:String, path:String, preventEscape:Bool) -> String
Convert a path into an absolute path at the given base. Throws an error if the
realative path uses '..'
to be outside of basePath
when preventEscape
is
true
. preventEscape
defaults to true
.
absolutizePath("/Home/", "app.config") -> "/Home/app.config"
baseDirectory
baseDirectory(path:String) -> String
The directory portion of the path.
baseName("/public/danielx.net/index.html") -> "/public/danielx.net/"
baseName
baseName(path:String) -> String
The base name of a file. This strips all directories from the path.
baseName("/public/danielx.net/index.html") -> "index.html"
extensionFor
extensionFor(path:string) -> string
extensionFor("README.md") -> "md"
extensionFor("/public/index.html") -> "html"
extensionFor("notes") -> ""
normalizePath
normalizePath(path:String) -> String
normalizePath("/public/danielx.net/..") -> "/public/"
normalizePath("/public/./danielx.net/") -> "/public/danielx.net/"
normalizePath("/public///danielx.net/index.html") -> "/public/danielx.net/index.html"
separator
Constant representing character to separate directories. '/'
textMediaType
textMediaType(path:String) -> MediaType
textMediaType("style.css") -> "text/css; charset=utf-8"
textMediaType("index.html") -> "text/html; charset=utf-8"
textMediaType("index.js") -> "text/javascript; charset=utf-8"
textMediaType("index.json") -> "application/json; charset=utf-8"
textMediaType("README.md") -> "text/markdown; charset=utf-8"
textMediaType("app.coffee") -> "text/plain; charset=utf-8"
withoutAllExtensions
withoutAllExtensions(path:String) -> string
withoutAllExtensions("awesome.test.js") -> "awesome"
withoutExtension
withoutExtension(path:String) -> string
withoutExtension("awesome.test.js") -> "awesome.test"
Every file system has the following methods:
read
(path) -> Promise<blob>
write
(path, blob) -> Promise
delete
(path) -> Promise
list
(path) -> Promise<(FileEntry|FolderEntry)[]>
The primary file systems are backed by indexeddb using Dexie for local storage within the browser and S3 on AWS for persistence across machines.
There is also a special pkg
file system that mounts a json package structure
as a virtual directory. A compilers option can be given that will auto-compile
the source files into the destination when they change.
The last file system is named mount
which mounts any of the other systems
at specific paths and proxys and rewrites the paths to them. It also rewrites
and proxies the emitted events on the way out.
Paths are strings. Paths that end with a /
are folders.
/
is the file separator character.
Every file system also emits the following events:
"read", path
"write", path
"delete", path
"list", path
These events are emitted after a successful operation has completed. One common use for the events is refreshing a file in a window when the path is written, like the wiki viewer should update when the file it is rendering has updated.
The path in the event is a normalized absolute path in the file system.
read
Read a blob from a path, returns a promise fulfilled with the blob object. The
blob is annotated with the path i.e.: blob.path == path
(path) -> Promise<blob>
write
Write a blob to a path, returns a promise that is fulfilled when the write succeeds.
(path, blob) -> Promise
delete
Delete a file at a path, returns a promise that is fulfilled when the delete succeeds.
(path) -> Promise
list
Returns a promise that contains an array of mixed FileEntry and FolderEntry objects. The list is not recursive but the folders can be recursed with a utility function.
(path) -> Promise<(FileEntry|FolderEntry)[]>
FileEntry Interface
path: string
size: number
type: string
updatedAt: Date
FolderEntry Interface
folder: true
path: string
fs.Dexie
fs.Dexie(databaseName:String) -> fs.Dexie
Create a database that uses the browser's local indexeddb. Any file type can be stored. The total storage space is dependent on the browser and the user's local system. Sometimes the db can be removed when cleaning up browser data for websites leaving nothing behind.
dfs = DexieFS("test")
dfs.write "/test.txt",
new Blob ["heyy"],
type: "text/plain"
fs.Mount
A filesystem that mounts and delegates to subsystems.
Example: Mount many different kinds of file systems to different paths.
{Mount, Dexie, Fdfs, Storage} = system.fs
fs = Mount()
fd = new FormData
fdfs = Fdfs(fd)
fs.mount "/", s3fs
fs.mount "/game/", fdfs
fs.mount "/local/", Storage(localStorage)
fs.mount "/session/", Storage(sessionStorage)
fs.mount "/local/indexeddb/", Dexie()
The longes path wins, so reading from /local/indexeddb/
will not touch the
localStorage FS. Similarly all the other paths will not touch the S3FS, but any
other path not listed will since the s3fs was mounted at the root "/"
.
fs.S3
fs.S3(id:IdentityId, bucket:AWS.S3, refreshCredentials:->Promise)
Creates a file system interface backed by an S3 bucket. The S3 bucket needs to be configured properly as well as the AWS Cognito Identity Pool. This is a one time setup for creating the storage for all users for whimsy.space. S3 Setup Doc
id
the Cognito Identity Id for the user.
bucket
the instance of AWS.S3 bucket.
refreshCredentials
a function to call when credentials have expired that
returns a promise that resolves successfully when the credentials have been
refreshed. This is so we can retry reading/saving seamlessly if credentials
expire.
Example mounting the S3 FS to /My Briefcase/
in the OS:
id = AWS.config.credentials.identityId
bucket = new AWS.S3
params:
Bucket: "whimsy-fs"
refreshCredentials = ->
# This has the side effect of updating the global AWS object's credentials
cognito.cachedUser()
.then (AWS) ->
# Copy the updated credentials to the bucket
bucket.config.credentials = AWS.config.credentials
.catch console.debug
s3fs = fs.S3(id, bucket, refreshCredentials)
os.fs.mount "/My Briefcase/", fs
pkg
crudeRequire
crudeRequire(program:string)
Simplest require of a common js style module with no dependencies. Evaluates the
program and returns what is assigned to module.exports
.
crudeRequire("""
module.exports = "cool"
""") # -> "cool"
exec
exec(program:string, env, context)
Convenience wrapper around Function(args..., program).apply(context, params)
to execute a program string within a given environment context and return the
result.
htmlForPackage
htmlForPackage(pkg, opts) -> Blob
opts
additionalDependencies: url[]
code: string
stylesheets: url[]
systemConfig: SystemConfig
Construct an HTML file for the package.
The default behavior is to load the package's entry point but can be modified
to run any file in the package by overriding the code
option. The default is
require('./main')
. The code is wrapped with the system wrapper and the require
wrapper. The system wrapper calls: require("!system").launch(...)
with the
code in the callback and the require wrapper sets up the module system so that
require calls work.
It also adds remote dependencies to the HTML head and wraps with the system launch if present.
Package
Package data structure
source: Record<string, File>
distribution: Record<string, File>
config:
name: string
version: string
title: string
description: string
lang: string
iconURL: URL
manifest: boolean
dependencies: Record<string, URL>
dependencies: Record<string, Package>
entryPoint: string
remoteDependencies: URL[]
stylesheets: URL[]
type File =
content: string
ui
applyStyle
- AceEditor
- Action
- Bindable
- ContextMenu
- Drop
- Jadelet
- Menu
- MenuBar
- Modal
- Model
- Observable
- Progress
- Style
- Table
- Window
applyStyle
applyStyle(styleContent:string, className:string)
Finds or creates the style element in document.head
with the given class name
then sets the content of that element to the passed in style string. By using
the same class name you can have separate styles for system base styles, app
styles, custom user styles, and only replace the ones that change which makes
hot reloading easier.
window
window
x: Observable<number>
y: Observable<number>
width: Observable<number>
height: Observable<number>
title: Observable<string>
minimized: Observable<bool>
maximized: Observable<bool>
iconURL: Observable<url>
iconEmoji: Observable<string>
menuBar: MenuBar | Element
content: Observable<Element>
Methods
close
remove from DOM and trigger close event.maximize
expand to fill the entire screen.minimize
shrink to be only the title bar.raiseToTop
called automatically when interacted with to raise above other windows.restore
Events
close
maximize
minimize
resize
util
Postmaster
deprecationWarning
groupBy
lazyLoader
loadScripts
throttle(wait, fn)
promiseChoke
urlSafeSHA256
Postmaster
Reference to the Postmaster library. Useful for setting up communication between iframes or sub-windows.
postmaster = Postmaster
delegate:
save: ->
exit: ->
remoteTarget: ->
iframe.contentWindow
postmaster.invokeRemote method, params...
.then (result) ->
Receive messages by setting delegate
. Send messages with invokeRemote
. The
iframe must have a properly set up Postmaster instance inside. The iframe can
send messages to the delegate as it chooses.
OS
OS is the layer of operating system functionality built on top of the system libraries. It handles things like mounting file systems, launching applications, managing the desktop and windows, and communicating events between applications.
- Achievement
- I
- PACKAGE
- appById
- appData
- attachApplication
- cachedOrFetchAppPackage
- compileFile
- compileTemplate
- copyFile
- copyFileSelection
- createPackage
- dataForPackage
- deleteFile
- embalm
- embalmSymbol
- executeInIFrame
- executePackageInIFrame
- explorerSelection
- explorePackage
- findDependencies
- fs
- handlers
- initAppSettings
- installApp
- installAppFromFileSystem
- installAppHandler
- kill
- launchAppByAppData
- launchAppByName
- launchAppByPath
- launchAppBySource
- launchIssue
- loadProgram
- loadProgramIntoPackage
- moveFile
- moveFileSelection
- open
- openFolder
- openBriefcase
- openPath
- openersFor
- packageProgram
- readFile
- readAsText
- readAsJSON
- readTree
- registerHandler
- removeApp
- removeHandler
- require
- runningApplications
- saved
- tell
- updateFile
- uploadToCDN
- version
- writeFile
Jadelet
- compile
- dispose
- _elementCleaners
- exec
- parse
- release
- retain
compile
compile(source, opts)
Compile a Jadelet template source string and return a string containing code that can be executed in common js format.
Ex.
module.exports = system.ui.Jadelet.exec(<parsedAst>);
This compiler is used in editors to create a distribution file from Jadelet source code. That's why it needs the exports and exec.
exec
exec(ast:string|JadeletAST) -> function(context) -> HTMLElement
If a string is given parse that string and return a render function. The render function takes a context object and return an hmtl element with bindings applied to the context.
parse
parse(source:string) -> JadeletAST
Parse a Jadelet source string and return an AST or throw an error.