-
Notifications
You must be signed in to change notification settings - Fork 4
Internal API
The APIs meant for apps, notably Device Storage, are not implemented directly on top of S3 (which is where we store all files). Instead, they are built on top of an internal API.
Let's say you want to use fs.putFile()
. All fs.*
functions are implemented in core.js
. That means that if you want to use it inside core.js
, you can just call putFile()
(without fs.
).
If you want to use it in laskyawm.js
, though, you might have to define it first (if it isn't already). Look at getFile
for inspiration.
If you want to use it in compat.js
, you have to say airborn.fs.putFile()
.
Similarly for core.*
functions, except those aren't accessible inside compat.js
.
Similarly for wm.*
functions, except those are defined in laskyawm.js
.
To add a fs.*
function, define a global function in core.js
, add its name to the whitelist in core.js
, and add a call to addAction()
in compat.js
.
To add a wm.*
function, define a global function in laskyawm.js
, add a case in the 'message' event handler, and add a call to addAction()
in compat.js
.
fs.getFile(file, [options], [callback(contents, err)])
Read a file.
file
is an absolute path to the file.
options
is an optional object.
options.codec
is the encoding you want to get the file decoded from. The default is 'utf8String'
. Supported codecs:
arrayBuffer
base64
-
base64url
: Same as base64, but with a slightly different alphabet. -
blob
: Currently only supported inputFile()
(and then only incompat.js
), not ingetFile()
. This codec automatically setsattributes.type
toblob.type
. -
dir
: The internal encoding of directories. Decoded, this is an object with filenames as keys and an object with attributes as values. Encoded, this is currently yaml, but don't rely on it. You can rely on support forDate
objects, though. hex
-
json
andprettyjson
-
raw
: Use this if you don't care what you get, as long as you can pass it toputFile
. Currently the same assjcl
. -
sjcl
: The internal encoding used by sjcl, the encryption library we use. -
utf8String
: An UTF8 string. -
yaml
: Encodes an object to yaml and decodes vice versa.
If there was a network error, or the file could not be decrypted, or the file could not be decoded as valid utf8/json/etc., contents
is null
and err
is an object with 'status'
and 'statusText'
properties.
fs.putFile(file, [options], contents, [attributes], [callback(err)])
file
is an absolute path to the file.
options
is an optional object. If you don't pass options
, though, contents
has to be a string.
options.codec
is the codec you want to encode contents
with. The default is 'utf8String'
.
attributes
is an object with attributes to be associated with the file.
If you want to store non-standard attributes, please store them in an object called attributes.user
(for file attributes settable by a user, such as mp3 ratings) or attributes.x
(for anything else). If the attributes are very specific to your app, you can also put them in attributes.x.nameOfYourApp
. Keep in mind that other apps can still access and modify those attributes, though, so don't trust their contents.
If you overwrite an existing file, the attributes are added to the ones the file already has. This also works for nested objects (e.g. attributes.user
). If you want to delete an attribute, add it in the object with a value of undefined
.
If there was a network error, err
is an object with 'status'
and 'statusText'
properties.
Note: when calling putFile()
multiple times, uploading files happens in stages. All calls to callback
s will probably be clustered at the end of the upload process, making them useless for a progress indication.
Warning: getFile()
and putFile()
are prone to race conditions. For example, in compat.js
, don't do something like this:
// This function by itself is okay:
function updateSettings(key, value, callback) {
airborn.fs.getFile('/tmp/settings', {codec: 'json'}, function(settings) {
settings[key] = value;
airborn.fs.putFile('/tmp/settings', {codec: 'json'}, settings, callback);
});
}
// But if you call it twice...
// settings = {a: 1}
updateSettings('b', 2); // settings = {a: 1, b: 2}
updateSettings('c', 3); // settings = {a: 1, c: 3} (Oops!)
// Instead, put the calls in a queue and run them sequentially.
// In core.js, the above code is safe though.
fs.prepareFile(file, callback(contents), progress(filesDownloaded, totalFiles))
Because all html, js and css files of an app are stored in the user's Airborn itself, we can't slap an html file in an iframe and be done with it. All references to js and css files have to be followed and replaced with an inline url. Currently, a combination of data:
URLs and Object URLs is used, although we hope to switch to Service Workers at some point.
fs.prepareFile
downloads a file and passes it to fs.prepareString
.
Warning: see warning for fs.prepareString
.
fs.prepareString(contents, options, callback(contents), progress(filesDownloaded, totalFiles))
Scans an html or css string (contents
) for references to other app-local files, downloads them, and replaces them with a data:
uri as returned by fs.prepareUrl
.
Warning: see warning for fs.prepareUrl
.
fs.prepareUrl(url, options, callback(url), progress(filesDownloaded, totalFiles))
Downloads the file at url
.
If url
is relative, it is resolved relative to options.relativeParent
.
If url
is absolute, it is resolved relative to options.rootParent
.
If url
is a html file, and you pass options.csp
, a meta tag with that CSP is added.
Note: prepareUrl
will, when url
is a .html
file, use a two-step bootstrap process. It will not call progress()
, and instead call callback()
with a simple html file which will call fs.prepareFile
with options.bootstrap
set to false
, and will also call wm.showProgress
/wm.setProgress
/wm.hideProgress
.
Warning: totalFiles
is not stable across calls to progress
. As more files are discovered that have to be prepare
d, totalFiles
might increase. Thus, filesDownloaded / totalFiles
might decrease. However, if you're going to pass that number to wm.setProgress
, that function already takes care of this situation, by ignoring decreased values of its first argument.
fs.startTransaction()
If you're going to do multiple file operations, you can wrap them in fs.startTransaction()
and fs.endTransaction()
. Currently, this doesn't have much effect: S3 doesn't support transactions, and neither do we (at the moment).
It does have a (small) performance benefit: normally, after a call to putFile()
, Airborn will wait (100 ms, currently) for more calls to putFile()
and automatically wraps them together in what it calls a transaction (allowing it to update the folder hierarchy only once). If you call these functions manually, you don't pay these 100 ms.
Note: as long as you haven't called fs.endTransaction
(or it is called automatically, after 100 ms, but don't depend on this happening at all) no callback
s for putFile()
will be called. So don't, for example, wait until all callback
s for putFile()
have fired before calling endTransaction
.
Warning: calls to startTransaction
and endTransaction
cannot currently be nested. For example, if you call startTransaction
twice and endTransaction
once, all files written up to that point will be uploaded and a second call to endTransaction
will have no effect.
End a transaction as started by fs.startTransaction
.
fs.listenForFileChanges(listener(path, reason))
path
is the absolute path that has changed. This may be either a file or a directory, in which case it means any of its descendants have been added, changed, or removed.
reason
is one of:
'added'
'modified'
-
'deleted'
(Not currently implemented.)
wm.focus()
Brings the window to the foreground.
wm.reportClicked()
Report that the user has clicked inside the window. Please do not use this unless that actually happened. If wm.focus
does not do what you want, propose to add another API function.
wm.setTitle(title)
Set the title of the tab. Currently this also changes the browser window's title with core.setTitle
.
wm.setIcon(url)
Set the icon of the window. Currently this does not change the browser window's icon.
wm.openFile(path, [target], [options])
Will probably be deprecated by Web Activities.
Open the file at path
in an appropriate viewer or editor.
target
can be:
- An action, as defined in
filetypes
, such as'view'
,'edit'
or'install'
. - The string
'replace'
(if called fromcompat.js
), in which case the file is opened in the calling tab. - A path to an app, such as
'/Apps/fancyedit/'
.
options
can only be used from laskyawm.js
.
If options.callback
is set, it is called with (iframeWin, tab, div).
The options of wm.openWindow
also work here.
wm.openWindow(appPath, options, callback(iframeWin, tab, div))
appPath
is a path to an app, such as '/Apps/myfancyapp/'
.
If options.targetDiv
is not set, a new window is opened.
If options.targetDiv
is set and options.innewtab
is set, a new tab in the existing window is opened.
If options.targetDiv
is set and options.targetTab
is set, the app is loaded in that tab.
If options.originDiv
is set, the new window is positioned near that window.
The options from wm.showProgress
/wm.setProgress
/wm.hideProgress
also work here.
Note: there is also an openWindow()
in core.js
which does something similar but has a different signature, isn't part of this API and is only used to open laskyawm.html
.
wm.showProgress(options)
Start showing a progress bar.
options.loaderElm
is the loader element to use. For example, around the location bar is a loaderElm with the class 'loader'
.
wm.setProgress(fraction, options)
Set the location bar progress to fraction
(from 0 to 1).
The options from wm.showProgress
also work here.
wm.hideProgress(options)
Stop showing a progress bar.
If options.loaderHighlight
is not false
, it also shows a brief fadeout animation indicating that an operation has finished.
The options from wm.showProgress
also work here.
core.setTitle([title])
Set the browser window's title to title + ' - Airborn'
, or simply 'Airborn'
if you pass a falsy (empty) title
.
core.setIcon(url)
Set the browser window's favicon to url
.
core.logout()
Logout from Airborn and refresh the page.
Note: the window manager should probably individually close all windows first, to give windows a chance to alert about non-saved changes. laskyawm
doesn't do this yet, though.