UE4 Integration 2.00
The FMOD UE4 Integration is a plugin that allows you to use the FMOD APIs and projects from the FMOD Studio authoring tool in your UE4 game.
Normally you will just need one copy of the integration. If you develop for multiple platforms at once, you can copy multiple integrations over the top of each other.
The integration consists of a single FMODStudio folder which can be placed in either the Engine/Plugins
directory or your UE4 game's Plugin directory {ProjectName}/Plugins
. The next steps show how to install it into your Engine directory, so it will be available for all projects using UE4.
Browse to your UE4 Engine folder and unzip FMODStudio into the plugins directory.
Use Finder to browse to your /Users/Shared/UnrealEngine/4.X/Engine
directory and drop FMODStudio into the plugins directory.
If you have trouble getting the plugin working on a certain platform, then try putting FMODStudio in your game's Plugins directory instead of the engine.
Otherwise see the TroubleShooting section.
Start by replacing the old FMODStudio folder with the new version:
If you are updating to a newer minor version of FMOD no additional steps are required unless specified in the revision history.
Upgrading to a newer major version of FMOD is usually only recommend for projects at or near the beginning of development, because new major versions may introduce behavioral and breaking changes. If you are upgrading to a new major version, you will need to read over:
These will describe specific changes that might need to be made to your project.
There are settings in both UE4 and FMOD Studio that need to be configured to link the two together.
If you have any trouble setting up your project, use the automatic method. These settings can always be changed later.
You can run the "Help > FMOD Validate" option in the UE4 Help menu. It finds and fixes common issues, and can automatically set up your FMOD Studio project for you!
It will check the following:
From your FMOD Studio Project, select "Edit > Preferences..." ("FMOD Studio > Preferences..." on Mac) and select the build tab. Set your built banks output directory to a directory called "FMOD" under your game's content path.
Now select "File > Build". This will build bank files for events that have been assigned to banks. You should do this whenever project data has been modified.
Now, open UE4 and look at the content browser. The plug-in defaults to looking in Content/FMOD
directory for banks, so if you have exported banks there, assets should appear in the content window automatically. These represent items in your Studio project which update automatically when banks are built.
For more information about banks, see the Banks page.
If you want to recompile the plugin, you can drop the plugin into a code project under your game's Plugins/FMODStudio
directory, then re-generate the project. This might be useful if you want to use the plugin with a different version of the engine, such as a new pre-release of UE4. You can also do this if you want to get the plugin from github.
To recompile the plugin after downloading it from FMOD, do the following:
FMODStudio/Intermediate
directory.FMODStudio/Binaries/Platform/UE4*.*
files. Leave the fmod libraries in the binaries directory!YourGame/Plugins/FMODStudio
.To compile the plugin after downloading the source from github, do the following
FMODStudio/Binaries/Platform/
directory. The libs can be obtained in the programmers API download or from the FMOD for UE4 download.YourGame/Plugins/FMODStudio
.When rebuilding the plugin inside a code project, make sure you haven't also left it in the engine directory as well!
You are able to interface with the FMOD UE4 Integration and/or the FMOD C++ APIs.
To reference FMOD Studio, the programmer will need to add the following to their .Build.cs file:
PrivateDependencyModuleNames
To add some FMOD Events to a class, do the following:
UPROPERTY
macro like any other fieldTo play the event at a location, do the following:
You can also call UFMODBlueprintStatics::PlayEventAttached to create a new audio component attached to an actor, which will update the location automatically as the actor moves around the world.
Programmers can interface with FMOD Studio directly by including "fmod_studio.hpp".
The Studio system can be obtained by GetStudioSystem. The function takes an enum because there may be a separate Studio system for auditioning in-editor and the proper system for play-in-editor. Normally, you will want to obtain the system with EFMODSystemContext.Runtime since that is the real system used in game.
if (IFMODStudioModule::IsAvailable())
{
FMOD::Studio::System* StudioSystem = IFMODStudioModule::Get().GetStudioSystem(EFMODSystemContext::Runtime);
if (StudioSystem)
{
// Use it here
}
}
You can use a mixture of FMOD Studio wrapper and FMOD Studio API functions. For example:
// Call wrapper helper function to create and start an event instance
FFMODEventInstance InstanceWrapper = UFMODBlueprintStatics::PlayEventAtLocation(ThisActor, MyEvent, FTransform(MyLocation), true);
FMOD::Studio::EventInstance* Instance = InstanceWrapper.Instance;
// Call into FMOD API directly
Instance->setVolume(0.5f);
// The instance handle will be cleaned up automatically when the sound finishes
For further documentation, see:
- Integration API Reference
- FMOD API Reference.
The FMOD UE4 integration provides multiple ways in which Studio events can be played.
The simplest way to play a looping ambience, is to drag and drop an event from the content browser into a scene viewport.
For example, try dragging the Game/FMOD/Events/Ambience/Forest
event into a level. This will create an FMODAmbientSound. Hit Play to begin playing in editor, and you should immediately hear the Forest ambience.
Make sure you drag an event into the main viewport. Dragging a bank into main viewport won't do anything.
Another easy way to trigger a sound is via blueprint. You can use the play event at location function to quickly trigger any given event.
In the example shown below, the Single_Explosion event is triggered at the location of the camera, every time the spacebar is pressed.
Keep in mind that more advanced control is also available from blueprints. There are graph functions for playing and stopping events, setting parameters, and loading or unloading banks. You can also add FMODAudioComponents to blueprints, allowing you attach audio directly to an object.
FMOD can support up to 8 listeners in game. The FMODListeners will follow the UE4 listeners which by default is attached to the camera, but we can move them by moving the UE4 listeners.
This is particularly useful for Third-Person and Top-Down style games.
Using SetAudioListenerOverride allows you either attach the listener to a component or set the transform and rotation manually.
Content created in FMOD Studio is exported into bank files. These bank files can then be loaded within Unreal using the FMOD UE4 integration. Banks can contain multiple events, which will implicitly pull in any audio assets they depend on.
Loading a bank will load all metadata, which contains information about all the events, parameters, and other data needed for all events assigned to that bank.
It is highly recommended that banks are exported to the Content directory of your project (see Deployment for more information). This can set via the built banks output directory setting in the FMOD Studio, which can be found in "Edit > Preferences..." on Windows (or "FMOD Studio > Preferences..." on Mac), under the Build tab.
When using the UE4 editor, as long as you match the FMOD Studio built banks output directory to the bank output directory specified in the Unreal project settings ("Edit > Project Settings > FMOD Studio"), the integration will find and load all bank content automatically.
Before a new FMOD Studio event can be used in Unreal, it must first be assigned and built to a bank which can be loaded by Unreal. This can be done within FMOD Studio via the context menu of an event, or by dragging and dropping an event onto a bank.
Events are typically assigned to the same bank when they should be loaded and unloaded at the same time. For example, you might put all the events for the Goblin enemy within the Goblin bank.
Once you have assigned your events to a bank, you should rebuild your banks. This is done via the "File > Build..." menu item.
The banks built in FMOD Studio are loaded in editor by the plugin, so that you can browse Events, Buses, Snapshots, etc. from the Studio Project. You are able to customize the way the banks are loaded in game, to suit your requirement, otherwise by default all the banks will be loaded at initialization.
Within the Unreal editor, banks are loaded automatically as soon they are built. When correctly configured, any data within banks (e.g. events, mixer strips) should appear within the content browser under Game/FMOD
by default.
The FMOD UE4 integration will load all banks by default. If you would like to manually control bank loading, this behavior can be disabled via the load all banks checkbox withing the FMOD Studio settings dialog ("Edit > Project Settings > FMOD Studio").
If using split banks, make sure to load the assets bank first and using load sample data on the metadata bank.
Banks can then manually be loaded and unloaded using the Load Bank
and Unload Bank
blueprint functions.
The Master Bank does not need to be loaded manually. It is automatically loaded at startup.
FMOD is integrated into Unreal Engine 4's Sequencer.
Events can be added in one of two ways:
Ambient sounds already placed in the level may be possessed by the level sequence. Add ambient sound actors to the sequence by clicking the button in the Sequencer editor and choosing the ambient sound actor to add. Alternatively the actor can be dragged from the World Outliner into the Sequencer editor.
Possessed events will retain any state set by the level sequence when playback is complete. The level sequence's Restore State setting can be enabled to restore the state of possessed events (and any other actors possessed by the level sequence).
New events may be spawned from Sequencer. Sequencer can spawn FMOD events during playback. To create a spawned event drag an FMOD event from the Content Browser into the Sequencer editor.
Spawned events will not automatically play when spawned.
Once added to a sequence additional sub-tracks are required to do anything interesting. Sub-tracks can be added by clicking the button in the object's track. FMOD adds two new sub-track types for events in addition to the standard Sequencer sub-tracks.
Keyframes on the event control sub-track can be used to Play or Stop the event.
An FMOD Event Parameter Track allows additional sub-tracks to be added for each parameter in the targeted FMOD event. Additional sub-tracks can be added by clicking the button in the FMOD Event Parameter Track.
Keyframes may be added to the parameter sub-tracks to control the value of the event parameter during playback of the level sequence. The Unreal Engine 4 curve editor may be used to create rich curves for FMOD event parameters.
The FMOD UE4 integration is unable to validate the range of parameter values set by Sequencer. The FMOD Engine will clamp any parameter value outside the range specified in FMOD Studio.
The FMOD UE4 integration supports the use of ray casts, to drive a specified parameter, for per instance occlusion of sounds.
To enable occlusion ray casts for FMOD in your UE4 project, set the name of the parameter that will be used for occlusion in Studio.
If an Event contains this parameter, the integration will set the parameter value any time the occlusion value changes.
You can disable occlusion, per instance, and adjust the Trace Channel in the Component Details window.
The FMOD UE4 integration supports the use of the standard UE4 audio volumes to trigger Studio's advanced snapshot system.
The workflow to use reverb zones is to set up snapshots in FMOD Studio. Snapshots can modify global reverb effects, change any bus volume, and modify any DSP value. To help trigger snapshots for reverb effects, the integration exports all snapshots as reverb effects in the FMOD/Reverbs
folder.
These reverb effects can be dragged into audio volume Reverb Settings panel to be triggered when the audio listener enters the audio zone. It uses the same logic as the inbuilt UE4 audio system to determine which audio volume should be enabled, based on the priority of the volume.
By default, snapshots apply instantly. To have a snapshot fade in, one of two things can be done. The first is by adding a AHDSR modulation to the intensity dial. The second way is to expose the intensity as a parameter, which allows it to be driven from the integration.
If the snapshot has its intensity exposed as a parameter, then the integration will ramp in the intensity over time based on the audio volume's Volume and Fade Time settings. If the snapshot does not expose its intensity as a parameter, then these values will not do anything.
Another feature of the UE4 audio system is the ability to have an ambient effect applied to selected instances, based on both the listener position and the emitter position. Unlike the global reverb effects, this is something which is applied per instance.
Only some sounds should be affected by the ambient settings. To enable the ambient effect your Events will need two parameters, one for volume and one for LPF.
You will need to add these parameter names to the integration settings.
If an Event contains these parameters, the integration will set the parameter value any time the ambient values change.
Only FMOD audio components are affected by ambient zones. The simpler PlayEventAtLocation
blueprint function to spawn one-shots does not apply ambient effects.
You can hook up event callbacks using blueprints. FMOD Audio component callbacks are only triggered if the enable callback option is ticked. This is because each component that triggers callbacks can incur a small CPU overhead, so it has to be turned on explicitly for the components you want to use.
Once enabled, then tempo beat callbacks and timeline callbacks can be added in blueprints. One way is via the Assign On Timeline Beat
and Assign On Timeline Marker
blueprint actions. For FMOD audio components used in blueprint actors, you can add events from the details window instead.
You can trigger various actions to occur on the beat or when a timeline hits a named marker. The event contains the same fields as FMOD_STUDIO_TIMELINE_BEAT_PROPERTIES
and FMOD_STUDIO_TIMELINE_MARKER_PROPERTIES
.
Localized audio tables are a special kind of audio table with features that facilitate localization. We recommend using localized audio tables if your game supports multiple spoken languages, or if you intend to add support for additional languages in a future patch.
Audio Tables are lists of audio files stored outside your FMOD Studio project's asset folder. You can use audio tables to control localized sounds. See the Dialogue and Localization section of the FMOD Studio Docs on how to set up an audio table in your project.
Audio tables are assigned to an associated bank, this means that in order to change the currently loaded audio table you will need to change the bank. Only one localized bank should be loaded at a time, otherwise just the first one to be loaded will be used.
You will need to define the different locale names and codes in the Localization Settings.
Only the locale that is selected as default will have it's bank loaded at startup, if Load All Banks has been enabled in the settings.
To change the locale, you will need to:
FMOD Studio events can include programmer sound modules that are controlled at runtime. There are a few different ways of hooking them up.
With this approach, you don't need to do any programming at all!
Create an event with a programmer sound module on it. If the module has a name, then if nothing else is assigned then that sound will be used. For example, if the sound designer sets the module name as "Welcome", then the audio table entry "Welcome" will be used by default.
To select at runtime what audio entry to use, set the programmer sound name field in the FMODAudioComponent.
Or you can assign the name via blueprint.
The name has to be one of the audio asset entries of a loaded audio table, or it won't find the sound to play.
Be careful to set the name before you play the audio component. If the name is assigned after the event has started, it may not play the right sound.
With this approach, you can easily play any media file for your event.
You can set up a programmer sound to point directly to a file. To do this, set the FMOD audio component's programmer sound name to the path to the .wav or .ogg file that you want to load. If this path is relative, it will be looked up relative to the content directory.
If you do this, you'll need to make sure that directory with the media files is added to directories to package in the packaging settings, otherwise it will work in the editor, but not when packaged into the final game.
With this approach, you have the most flexibility for programmers.
If you are writing code, you can programmatically set the FMOD Sound to use by calling the FMOD audio component function SetProgrammerSound
. Here is an example of setting the sound from code:
void AExampleGameMode::InitAudio(UFMODAudioComponent* AudioComponent)
{
if (AudioComponent)
{
FMOD::Studio::System* System = IFMODStudioModule::Get().GetStudioSystem(EFMODSystemContext::Runtime);
FMOD::System* CoreSystem = nullptr;
System->getCoreSystem(&CoreSystem);
// Create sound in memory
static const int EXAMPLE_SOUND_LEN = 4096;
float ExampleData[EXAMPLE_SOUND_LEN];
for (int i=0; i<EXAMPLE_SOUND_LEN; ++i)
{
ExampleData[i] = FMath::Sin((float)i);
}
FMOD_CREATESOUNDEXINFO SoundInfo = {0};
SoundInfo.cbsize = sizeof(SoundInfo);
SoundInfo.format = FMOD_SOUND_FORMAT_PCMFLOAT;
SoundInfo.defaultfrequency = 12000;
SoundInfo.numchannels = 1;
SoundInfo.length = sizeof(float) * EXAMPLE_SOUND_LEN;
FMOD::Sound* Sound = nullptr;
if (CoreSystem->createSound(reinterpret_cast<const char*>(ExampleData), FMOD_OPENMEMORY | FMOD_OPENRAW | FMOD_LOOP_OFF, &SoundInfo, &Sound) == FMOD_OK)
{
AudioComponent->SetProgrammerSound(Sound);
// Note: Need to remember to release the sound *after* the audio component has finished using it.
}
}
}
Also, when setting the name to an audio table entry, you will need to make sure the audio table bank is already loaded before the event starts.
The FMOD audio component only supports a single programmer sound per event. If you want to have an event that has multiple programmer sounds, each one playing a different sound, then you'll need to create the event directly via the FMOD API and provide your own callback. You can look at how the FMOD audio component programmer sound callback works and use that as a base for your own class.
These steps describe how to prepare your project for deployment. This is relevant to both the Launch option as well as the "File > Package Project" menu item.
If any platforms require specific steps, they can be found in Platform Specifics.
Banks need to be packaged and included in the game data. This can be done by selecting the "Edit > Project Settings..." menu item. Navigating to the Packaging section from the left hand pane, under the game heading, presents you with options for specifying directories that include extra assets. There are two ways of doing this:
We recommend using Directories to Package so that bank files are bundled into the package file automatically.
Each platform will look for its own type of banks in its own directory. Make sure you have added the platform to FMOD Studio project. The platforms are:
If you only have the Desktop banks and want to run on another platform, you can set force platform name to Desktop in the FMOD advanced settings.
The above directory name is relative to your Content directory. It is highly recommended that banks be placed within the content directory, as paths outside this directory will not deploy correctly to all platforms. For example:
This doesn't mean you need to put your whole Studio project inside the content directory. You can customize Studio by editing the Preferences and choosing a directory to export banks to, as described in the Working with Banks page.
The integration will load the platform bank files automatically. On PC it will load from FMOD/Desktop
but on Android and IOS it will look for banks under FMOD/Mobile
.
 To slim down your final package size, you may need to tweak the additional directories setting on a per platform basis. That way you only package FMOD/Mobile
for Android, FMOD/Desktop
for PC, FMOD/PS4
for PS4, etc.
You will need to make sure the plugins are deployed as well. Unreal deployment doesn't access to the settings information so you will need to create an extra file that lists the plugins you want to deploy.
Create a file "plugins.txt" in the FMODStudio/Binaries/Platform/
directory. The text file should contain the plugin names (just the name without file extension).
For example, to deploy fmod_gain.dll on Win64 builds, create a file FMODStudio/Binaries/Win64/plugins.txt
with the following contents:
fmod_gain
One issue to be aware of is where blueprints are serialized from disk too early, before any plugins are loaded. This can occur from the following code, which is included by default in example C++ projects constructor:
static ConstructorHelpers::FClassFinder<APawn> PlayerPawnClassFinder(TEXT("/Game/FirstPersonCPP/Blueprints/FirstPersonCharacter"));
The finder will serialize the first person character blueprint, but any FMOD references will fail to load since the FMOD plugin has not been created yet. To make sure that the FMOD plugin is loaded first, add the line of code above the class finder.
IFMODStudioModule::Get();
By default FMOD Studio works side-by-side with the inbuilt Unreal audio device on the following platforms:
Any of the platforms listed below will not work with the inbuilt Unreal audio:
To disable the Unreal audio while leaving the FMOD Studio audio, the standard Unreal ini file setting can be used.
For each required platform, add a new file /Config/{Platform}/{Platform}Engine.ini
with this section:
[Audio]
AudioDeviceModuleName=
The audio device can be disabled for every platform that you want to ship with.
The default permissions won't allow FMOD to set up a socket properly for live update. Uncheck the enable live update option in FMOD settings to avoid errors.
If you get a deployment error "resource.resw is in use by other process", go to the YourGame/Config/DefaultGame.ini
and remove the following:
-CulturesToStage=en
+CulturesToStage=en


Share your holiday dreams
And find the perfect partner to fullfill it
And find the perfect partner to fullfill it
Bradley Hunter
Based in Chicago. I love playing tennis and loud music.
Lucas Marsha
I get my inspiration from nature and objects around me. I have a passion to colours, typography and skateboards.
Heather Walker
I'm a happy person that loves cats and climbing on mountains.
Bradley Hunter
Based in Chicago. I love playing tennis and loud music.


La Spiaggia Tanning and Hair Salon
Welcome to La Spiaggia!
Are you planning a vacation or have a special event and want to look your best?
Then be sure to stop at La Spiaggia Tanning and Hair Salon in Toronto.
Our professional indoor tanning and hair services are for you.
Tanning not only makes you look your best it makes you feel your best, and provides helpful vitamin D.
Call us today to schedule an appointment and get a healthy tan.
About La Spiaggia
Hours of Operation:
Monday: Closed | Tuesday - Friday: 10:00 am - 8:00 pm | Saturday: 9:00 am - 5:00 pm | Sunday: Closed
Services:
Tanning (Bed and Spray)
Hair Services for Men, Women, and Children
Waxing Services for Men and Women
Facial and Skin Care
Contact for more information!
Walk-ins Welcome, Appointments Recommended!
Contact
2141 Kipling Ave., Unit 5A
Etobicoke, ON M9W 4K8
Located at Kipling Plaza; next to Giant Tiger
(647) 344-4441
gioele@laspiaggiasalon.ca


This is UVA
Illimitable Minds.
Endless Pursuit.
Illimitable Minds.
Endless Pursuit.
Illimitable Minds.
Endless Pursuit.
Illimitable Minds.
Endless Pursuit.
Illimitable Minds.
Endless Pursuit.
Illimitable Minds.
Endless Pursuit.


{{ name }},
Someone has asked to reset the password for your account.
If you did not request a password reset, you can disregard this email.
No changes have been made to your account.
To reset your password, follow this link (or paste into your browser) within the next 90 minutes:
{{ reset_url }}
Thank you,
Team Memo


Racing Point confirm driver line-up for post-season testing
by Tom MasonRacing Point has confirmed that Sergio Perez and Lance Stroll will drive for the time at the end of season test in Abu Dhabi. This comes as no surprise after both drivers were confirmed for the 2020 season by the team.
Perez will drive on the first day which is Tuesday, while his teammate Stroll will be in position on the second day which is Wednesday.
The test days take place after the climax of the season next weekend in Abu Dhabi.
With one race remaining, Racing Point is seventh in the constructors' standings on 67 points, 16 behind Toro Rosso.
The Mexican Perez is comfortably leading the battle between the two teammates, with 46 points to his name compared to Stroll's 21.


12 MONKEYS A film review by Steve Rhodes Copyright 1996 Steve Rhodes
RATING (0 TO ****): *
If you are of the school of thought that movies like medicine can only be good if they are painful to endure, then do I have a movie for you, TWELVE MONKEYS. It is a dark, depressing and ugly science fiction mystery involving time travel and schizophrenia. It is a movie that is about as much fun to watch as getting your teeth pulled, and yet the mystery is so compelling that your mind will not let your feet walk out of the theater early no matter how hard they try.
When the movie opens, it is the 21st century. As any Hollywood writer will tell you, in the next century we are all going to revert to acting like animals and our technology will be more like the past than the present plus most of the equipment will be covered in grime. Screenwriters David and Janet Peoples's script based on another one by Chris Marker tells of a future that is full of angry and mean people. It goes on to show a present and past that is equally mean spirited.  In this movie the sets for the present and the past are equally sinister and morose.
It seems that in 1996 someone, believed to be "the army of the twelve monkeys", unleashed a virus on the world that killed ninety-nine percent of the humans on the planet. The few survivors went underground, and the surface thereafter became inhabited only by wild animals. James Cole (Bruce Willis) is a prisoner who is picked out of his tiny cage by a crane and sent first to the surface in the present and later to the past to uncover hints as to exactly what the original virus was so that the 21st century scientists can get a clue as to how to make an antidote.
The 21st century scientists use old fashion devices more mechanical than electrical, and their time machine is not very accurate. First, James is sent by mistake to 1990 and then to the French trenches in World War I. Eventually they get it right, and he lands in 1996 right before the virus is unleashed.
Since the people in 1990 think he is schizophrenic and is making all this future plague and pestilence stuff up, he is sent to a mental institution where he meets fellow patient Jeffrey Goines (Brad Pitt). Jeffrey is the son of a famous virologist named Dr. Leland Goines (Christopher Plummer). Jeffrey is most certainly nuts, and James begins to think that he is too and that he is making it all up in his mind. While at the hospital he meets and forms a relationship with his psychiatrist, Dr. Kathryn Railly (Madeleine Stowe). Since the mystery is the only thing good about this disgusting but innovative show, I will not describe the plot more. Suffice it to say that it is convoluted, and there is lots of it.
 The sets in the past, present, and future are all depressing and apocalyptic.  His keepers kept him high on drugs so he spends most of the movie, not only dripping blood and drooling, but also stoned out of his mind. I found the bleak and lugubrious images of the show infectious. As I left the theater, I felt terrible - hopeless and depressed.
Director Terry Gilliam puts a strong stamp on a movie. I loved his BRAZIL, but hated his THE FISHER KING. Although I was intrigued by the TWELVE MONKEYS story, I was angry at myself for not having the good sense to walk out of it. Many times I started to leave since I was not enjoying myself at all and was finding the images painful to endure, but I kept wanting to know the resolution of the mystery, which I must admit is wrapped up nicely.
Besides the intriguing plot, there is some good acting even if the movie is pretty unbearable. Brad Pitt proved to me that he is more than just a pretty face. Willis, whom I frequently dislike, is quite good, but hard to watch. Stowe, whom I always like, is good and is one of the few characters in this movie that you not only like, but actually care about.
TWELVE MONKEYS runs way too long for my taste at 2:09. It is correctly rated R for gore and violence and a vision of the world that could give you nightmares for a long time. There is male nudity, but no sex. Do not let teenagers see this show unless they are quite mature. This is an ugly film that I wish I had never seen, and I certainly would not wish it inflicted on others. I am giving it a single * for some good acting and an intriguing mystery.
**** = One of the top few films of this or any year. A must see film. *** = Excellent show. Look for it. ** = Average movie. Kind of enjoyable. * = Poor show.  Totally unbearable.
REVIEW WRITTEN ON: February 8, 1996
Opinions expressed are mine and not meant to reflect my employer's.
The review above was posted to the
rec.arts.movies.reviews newsgroup (de.rec.film.kritiken for German reviews).
The Internet Movie Database accepts no responsibility for the contents of the
review and has no editorial control. Unless stated otherwise, the copyright
belongs to the author.
Please direct comments/criticisms of the review to relevant newsgroups.
Broken URLs inthe reviews are the responsibility of the author.
The formatting of the review is likely to differ from the original due
to ASCII to HTML conversion.
Related links: index of all rec.arts.movies.reviews reviews


@(Globals variables can contain preddefined values for the page content. In other words: you can define some keys with the content onetime and use them in different pages.)
$key
MAIN.variables


001/* 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, 013 * software distributed under the License is distributed on an 014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 015 * KIND, either express or implied. See the License for the 016 * specific language governing permissions and limitations 017 * under the License. 018 */ 019package org.apache.shiro.concurrent; 020 021import org.apache.shiro.SecurityUtils; 022import org.apache.shiro.subject.Subject; 023 024import java.util.concurrent.Executor; 025 026/** 027 * {@code Executor} implementation that will automatically first associate any argument 028 * {@link Runnable} instances with the currently available {@link Subject} and then 029 * dispatch the Subject-enabled runnable to an underlying delegate {@link Executor} 030 * instance. 031 * <p/> 032 * This is a simplification for applications that want to execute code as the currently 033 * executing {@code Subject} on another thread, but don't want or need to call the 034 * {@link Subject#associateWith(Runnable)} method and dispatch to a Thread manually. This 035 * simplifies code and reduces Shiro dependencies across application source code. 036 * <p/> 037 * Consider this code that could be repeated in many places across an application: 038 * <pre> 039 * {@link Runnable Runnable} applicationWork = //instantiate or acquire Runnable from somewhere 040 * {@link Subject Subject} subject = {@link SecurityUtils SecurityUtils}.{@link SecurityUtils#getSubject() getSubject()}; 041 * {@link Runnable Runnable} work = subject.{@link Subject#associateWith(Runnable) associateWith(applicationWork)}; 042 * {@link Executor anExecutor}.{@link Executor#execute(Runnable) execute(work)}; 043 * </pre> 044 * Instead, if the {@code Executor} instance used in application code is an instance of this class (which delegates 045 * to the target Executor that you want), all places in code like the above reduce to this: 046 * <pre> 047 * {@link Runnable Runnable} applicationWork = //instantiate or acquire Runnable from somewhere 048 * {@link Executor anExecutor}.{@link Executor#execute(Runnable) execute(work)}; 049 * </pre> 050 * Notice there is no use of the Shiro API in the 2nd code block, encouraging the principle of loose coupling across 051 * your codebase. 052 * 053 * @see SubjectAwareExecutorService 054 * @since 1.0 055 */ 056public class SubjectAwareExecutor implements Executor { 057 058 /** 059 * The target Executor instance that will actually execute the subject-associated Runnable instances. 060 */ 061 private Executor targetExecutor; 062 063 public SubjectAwareExecutor() { 064 } 065 066 public SubjectAwareExecutor(Executor targetExecutor) { 067 if (targetExecutor == null) { 068 throw new NullPointerException("target Executor instance cannot be null."); 069 } 070 this.targetExecutor = targetExecutor; 071 } 072 073 /** 074 * Returns the target Executor instance that will actually execute the subject-associated Runnable instances. 075 * 076 * @return target Executor instance that will actually execute the subject-associated Runnable instances. 077 */ 078 public Executor getTargetExecutor() { 079 return targetExecutor; 080 } 081 082 /** 083 * Sets target Executor instance that will actually execute the subject-associated Runnable instances. 084 * 085 * @param targetExecutor the target Executor instance that will actually execute the subject-associated Runnable 086 * instances. 087 */ 088 public void setTargetExecutor(Executor targetExecutor) { 089 this.targetExecutor = targetExecutor; 090 } 091 092 /** 093 * Returns the currently Subject instance that should be associated with Runnable or Callable instances before 094 * being dispatched to the target {@code Executor} instance. This implementation merely defaults to returning 095 * {@code SecurityUtils}.{@link SecurityUtils#getSubject() getSubject()}. 096 * 097 * @return the currently Subject instance that should be associated with Runnable or Callable instances before 098 * being dispatched to the target {@code Executor} instance. 099 */ 100 protected Subject getSubject() { 101 return SecurityUtils.getSubject(); 102 } 103 104 /** 105 * Utility method for subclasses to associate the argument {@code Runnable} with the currently executing subject 106 * and then return the associated Runnable. The default implementation merely defaults to 107 * <pre> 108 * Subject subject = {@link #getSubject() getSubject()}; 109 * return subject.{@link Subject#associateWith(Runnable) associateWith(r)}; 110 * </pre> 111 * 112 * @param r the argument runnable to be associated with the current subject 113 * @return the associated runnable instance reflecting the current subject 114 */ 115 protected Runnable associateWithSubject(Runnable r) { 116 Subject subject = getSubject(); 117 return subject.associateWith(r); 118 } 119 120 /** 121 * Executes the specified runnable by first associating it with the currently executing {@code Subject} and then 122 * dispatches the associated Runnable to the underlying target {@link Executor} instance. 123 * 124 * @param command the runnable to associate with the currently executing subject and then to execute via the target 125 * {@code Executor} instance. 126 */ 127 public void execute(Runnable command) { 128 Runnable associated = associateWithSubject(command); 129 getTargetExecutor().execute(associated); 130 } 131}


This is
Ozan Mirza's
Advanced Programmer's Workshop page.
GitHub
Return to main page!
Email
The first project I worked on this year was the class website while reorganizing the project structures. I was able to refresh my knowledge of webpaging. I also used object-oriented programming to automate the process of adding individual profiles to the website, which got rid of the ugly hard-codedness.


Michael A.
9/1/14
Went on the 4th of july and THIS WAS THE BEST EXPERIENCE EVER!!!! Really really REALLY cool instructors. Went up with just the two of us (plus our tandem instructors) in our little private plane dealio with a breathtaking scenic flight over the bay. They went over different things we could do on our free fall down... straight dive... flips... 360's... etc!
Ryan G.
9/1/14
If you're looking to jump out of a plane from 15,000 ft, look no further. As a "first time" the my nerves were through the roof but soon eased by the incredible staff at PCS. Everyone is super chill amd easy going. (DO NOT let the facility intimidate you, they're extremely professional) Many thanks to PCS for the amazing time. P.s. Buy the video...its $75. You will regret not buying later. Happy falling! :)


What are our hopes?
What are our challenges?
FacIal recognItIon bans
User space FLOSS
Non-commercIal decentralIzed Internet
PI-hole blocks ad querIes.
PI-hole Is a DNS server.
Raspberry PI Zero W connected to the Internet
RaspbIan Buster LIte Installed on mIcroSD card
USB power supply
Smart router
Laptop to Interface with RPI-Zero
1) Boot up Raspberry PI
2) SSH Into Raspberry PI
(user: pi
| password: raspberry
)
3) Type: curl -sSL https://install.pi-hole.net | bash
Select DNS upstream
Select IPv4 or IPv6
ConfIgure IP address(self-assIgned--thIs works most of the tIme)
Install web Interface
Keep admIn password
Go to:
http://
[your raspberry pI's IP]/admin
Change the DNS server on your devIce to the IP address of PI-hole
OR Update the DNS server on your router to the IP address of the PI-hole
THEN VIsIt a websIte
LIbrarIes can use PI-hole to protect patron prIvacy by blocking Internet ads, trackIng, and mal-ware.
It can reduce bandwIdth because It Is not loadIng ads.
It can block ads on many devIces at once IncludIng phones and TVs.
ACLU (2019, Jun. 3) Coalition letter calling for a federal moratorium on face recognition. ACLU. Retrieved from https://www.aclu.org/letter/coalition-letter-calling-federal-moratorium-face-recognition
Beadon, L. (Producer). (2019, Dec. 3). Techdirt podcast episode 233: Your secret consumer score [Audio podcast]. https://www.techdirt.com/articles/20191203/12402743497/techdirt-podcast-episode-233-your-secret-consumer-score.shtml.
Cox, J. and Koebler, J. (2019, Dec. 19).Inside the podcast that hacks Ring camera owners live on air. Vice, Retrieved from https://www.vice.com/en_us/article/z3bbq4/podcast-livestreams-hacked-ring-cameras-nulledcast.
Glass, E.R. (2019, Nov. 1). Open access in an age of surveillance technology. [Blog post]. Retrived from https://www.erinroseglass.com/open-access-in-an-age-of-surveillance-technology/.
Hardesty, L. (2018, Feb. 11). Study finds gender and skin-type bias in commercial artificial-intelligence systems. MIT News, Retrieved from https://news.mit.edu/2018/study-finds-gender-skin-type-bias-artificial-intelligence-systems-0212.
Hill, K. (2020, Jan. 18). The secretive company that might end privacy as we know it. New York Times, Retrieved from https://www.nytimes.com/2020/01/18/technology/clearview-privacy-facial-recognition.html.
Hill, K. (2019, Nov. 4). I got access to my secret consumer score. now you can get yours, too. New York Times, Retrieved from https://www.nytimes.com/2019/11/04/business/secret-consumer-score-access.html
King, E. (2019, Dec. 4). Chicago cops use social media to track grieving families of gunshot victims. Medium. Retrieved from https://onezero.medium.com/chicago-cops-use-social-media-to-track-grieving-families-of-gunshot-victims-e68e5a6dc40c
lady ada (2017, Aug. 15). Install Pi Hole. Adafruit. Retrieved from https://learn.adafruit.com/pi-hole-ad-blocker-with-pi-zero-w/install-pi-hole
Mac, R., Haskins C., and McDonald, L. (2020, Feb. 27). Clearview’s facial recognition app has been used by the Justice Department, ICE, Macy’s, Walmart, and the NBA. Buzzfeed. Retrieved from https://www.buzzfeednews.com/amphtml/ryanmac/clearview-ai-fbi-ice-global-law-enforcement
Simonite, and T. Barber, G. (2019, Oct. 17). The delicate ethics of using facial recognition in schools. Wired, Retrieved from https://www.wired.com/story/delicate-ethics-facial-recognition-schools/.
Zuboff, S. (2019). The age of surveillance capitalism. PublicAffairs.
Simonite, and T. Barber, G. (2019, Oct. 17). The delicate ethics of using facial recognition in schools. Wired, Retrieved from https://www.wired.com/story/delicate-ethics-facial-recognition-schools/.
Zuboff, S. (2019). The age of surveillance capitalism. PublicAffairs.


Building a RESTful API with Node, Flow, and Jest
This tutorial details how to develop a RESTful API with NodeJS, ExpressJS, and Flow using test-driven development (TDD).
We’ll be going full-Facebook with this application (FaceStack), utilizing:
- Flow for type checking
- Babel for transpilation
- Jest for our testing framework
- (Optionally) Yarn to replace NPM
Contents
Project Setup
Create a new directory to hold the project:
$ mkdir flow-node-api
$ cd flow-node-api
Transpilation
To start, let’s get Babel transpilation up and running. We’ll use Gulp to automate the build process.
NOTE: We’ll use yarn here to download and manage dependencies, but you can use
npm
just as easily if you wish. Anytime you see ayarn add
oryarn remove
just substitute in anpm install
ornpm rm
. The only difference is that yarn does the--save
part for you and withnpm
you must be explicit.
Go ahead and add gulp
, gulp-babel
, and gulp-sourcemaps
to your project, and create a gulpfile.js to start writing our Gulp tasks in:
$ yarn init -y
$ yarn add<EMAIL_ADDRESS><EMAIL_ADDRESS><EMAIL_ADDRESS>$ touch gulpfile.js
Also, install Gulp globally (if necessary), so you can run Gulp tasks from the command line:
$ yarn global add<EMAIL_ADDRESS>We’re going to write all of our source code in the “src” directory, so the first Gulp task will need to:
- Grab all of the JavaScript files inside of “src”
- Pipe the files through Babel
- Deliver them to the “build” directory
const gulp = require('gulp');
const babel = require('gulp-babel');
const sourcemaps = require('gulp-sourcemaps');
gulp.task('scripts', () => {
return gulp.src('src/**/*.js')
.pipe(sourcemaps.init())
.pipe(babel())
.pipe(sourcemaps.write('.'))
.pipe(gulp.dest('build'));
});
Pretty straightforward. Time to test!

Run the Gulp task, and then run the transpiled version of index.js:
$ gulp scripts
$ node build/index.js
Hello World!
Nice! However, do you really want to manually type gulp scripts
in to build the project every time changes are made? Of course not. So, let’s set up a watch
and a default
task with Gulp to make this easier.
Add the following to gulpfile.js
:
gulp.task('watch', ['scripts'], () => {
gulp.watch('src/**/*.js', ['scripts']);
});
gulp.task('default', ['watch']);
Now you can just run gulp
from the command line, and it will listen for changes to our JavaScript files inside of “src” and re-run the scripts
task whenever it detects changes.
Flow
Moving on, to use Flow, we’ll use the gulp-flowtype
plugin to interface with Flow. Download the dependency and head back over to gulpfile.js
.
$ yarn add --dev<EMAIL_ADDRESS>// ...
const flow = require('gulp-flowtype');
// ...
gulp.task('flow', () => {
return gulp.src('src/**/*.js')
.pipe(flow({ killFlow: false }));
});
// ...
// update the watch task as well
gulp.task('watch', ['flow', 'scripts'], () => {
gulp.watch('src/**/*.js', ['flow', 'scripts']);
});
This is all well and good, but we’re going to configure a few more parts before we move forward. We need to tell Babel to strip out all of our Flow type annotations. While we’re doing that, we might as well install the other Babel dependencies:
$ yarn add<EMAIL_ADDRESS>$ yarn add<EMAIL_ADDRESS><EMAIL_ADDRESS>With those installed create a .babelrc file in the root of the project, and add these settings:
{
"presets": ["latest"],
"plugins": ["transform-flow-strip-types"]
}
Finally, we need a .flowconfig to tell Flow that this is a project with Flow annotated code. If you have the Flow CLI installed, you can do this with flow init
. If you don’t, just create a file called .flowconfig file and paste this in:
[ignore]
[include]
[libs]
[options]
Whew. Now that we’ve done all that configuring, let’s make sure it’s all working by testing out some Flow type annotations. If you’re familiar with TypeScript, this syntax will look very familiar. There are some notable differences, but in general TypeScript and Flow look pretty similar. Let’s start with a simple function that adds two numbers together.
Run the default gulp task:
$ gulp
Replace the contents of src/index.js with the following:
// @flow
function testFunc(item) {
return 10 * item;
}
console.log(testFunc(2));
console.log(testFunc('banana'));
Since Gulp is watching for changes, you should automatically see the output from Flow as soon as you save the file:
src/index.js:4
4: return 10 * item;
^^^^ string. This type is incompatible with
4: return 10 * item;
^^^^^^^^^ number
Excellent! Flow is doing its job. Here, it’s telling us that when we try to call testFunc('banana')
we’re going to run into issues because testFunc
is clearly expecting its argument to be a number, not a string. Notice the // @flow
comment that’s now at the top of the file. This tells Flow that this file should be typechecked. If you don’t put this comment at the top of the file you’re working on, Flow will ignore it. Keep this in mind as you develop your application.
If you read the post on TypeScript (Developing a RESTful API With Node and TypeScript), you may already be wondering how we can use types with third-party libraries. Well, with Flow there’s a command line tool called flow-typed that is used to manage libdefs (library definitions) for Flow.
First, install flow-typed
globally:
$ yarn global add<EMAIL_ADDRESS>The nice thing about flow-typed
is that we don’t really have to manage it too much. It reads package.json
and automatically downloads the libdefs for our dependencies and stores them in “flow-typed”.
To install the libdefs for the packages we’re using so far just run:
$ flow-typed install --flowVersion=0.36.0
For packages that have no official libdef in the flow-typed repository, a stub is generated. Unfortunately, if you want to omit the --flowVersion=0.36.0
flag, you’ll need to install flow-bin
and have it listed as a dependency in package.json.
Before moving forward, we need to make one more change to our Gulp task for Flow. Now that we’ve got flow-typed
, tell Flow where we’re keeping these definitions:
gulp.task('flow', () => {
return gulp.src('src/**/*.js')
.pipe(flow({
killFlow: false,
declarations: './flow-typed'
}));
});
Great! We’ve got Flow type checking our code, and Babel is stripping out our type annotations and transpiling.
Let’s construct the basics of the server.
Server Setup
We’re going to use src/index.js as the entry point for our Express API along with the debug module to set up simple logging. Install it with yarn
(yarn add<EMAIL_ADDRESS>) or npm
(npm install<EMAIL_ADDRESS>--save
), and then wipe everything out of index.js and replace it with the following:
// @flow
'use strict'
import * as http from 'http';
import debug from 'debug';
import Api from './Api';
// ErrnoError interface for use in onError
declare interface ErrnoError extends Error {
errno?: number;
code?: string;
path?: string;
syscall?: string;
}
const logger = debug('flow-api:startup');
const app: Api = new Api();
const DEFAULT_PORT: number = 3000;
const port: string | number = normalizePort(process.env.PORT);
const server: http.Server = http.createServer(app.express);
server.listen(port);
server.on('error', onError);
server.on('listening', onListening);
function normalizePort(val: any): number | string {
let port: number = (typeof val === 'string') ? parseInt(val, 10) : val;
if (port && isNaN(port)) return port;
else if (port >= 0) return port;
else return DEFAULT_PORT;
}
function onError(error: ErrnoError): void {
if (error.syscall !== 'listen') throw error;
let bind: string = (typeof port === 'string') ? `Pipe ${port}` : `Port ${port.toString()}`;
switch (error.code) {
case 'EACCES':
console.error(`${bind} requires elevated privileges`);
process.exit(1);
break;
case 'EADDRINUSE':
console.error(`${bind} is already in use`);
process.exit(1);
break;
default:
throw error;
}
}
function onListening(): void {
let addr: string = server.address();
let bind: string = (typeof addr === 'string') ? `pipe ${addr}` : `port ${addr.port}`;
logger(`Listening on ${bind}`);
}
Alright. This looks like a lot of code, but it’s mostly boilerplate with some fancy type annotations added. Let’s break it down real quick though anyways:
- At the top we’ve got our Flow comment, imports, and our first bit of strictly Flow-enabled code - the
ErrnoError
interface declaration. This error type is used by Express. When using theflow check
command from the official command line tool, Flow will not flag this as an error. For whatever reason,gulp-flowtype
does. If you get a strange type check error, it may be worth it to install the Flow CLI and double check usingflow check
. - After the
ErrnoError
definition, we set up some data and instantiate the server by attaching our future Express app withhttp.createServer
. normalizePort
looks for the$PORT
environment variable and sets the app’s port to its value. If it doesn’t exist, it sets the port to the default value -3000
.onError
is just our basic error handler for the HTTP server.onListening
simply lets us know that our application has actually started and is listening for requests.
Run gulp
. Right now, you should see Flow complaining about trying to import the API:
src/index.js:7
7: import Api from './Api';
^^^^^^^ ./Api. Required module not found
This makes sense because we don’t even have a file called Api.js, so let’s create it and set up the basic structure for the API. In this file, the third-party libraries we’ll be using are:
- Express - web framework
- body-parser - JSON body parser for HTTP requests
- morgan - request logging
$ yarn add<EMAIL_ADDRESS><EMAIL_ADDRESS><EMAIL_ADDRESS>$ flow-typed install --flowVersion=0.36.0
$ touch src/Api.js
With the dependencies and libdefs acquired, we’re ready to build out the Api.js
file:
// @flow
import express from 'express';
import morgan from 'morgan';
import bodyParser from 'body-parser';
export default class Api {
// annotate with the express $Application type
express: express$Application;
// create the express instance, attach app-level middleware, attach routers
constructor() {
this.express = express();
this.middleware();
this.routes();
}
// register middlewares
middleware(): void {
this.express.use(morgan('dev'));
this.express.use(bodyParser.json());
this.express.use(bodyParser.urlencoded({extended: false}));
}
// connect resource routers
routes(): void {
this.express.use((req: $Request, res: $Response) => {
res.json({ message: 'Hello Flow!' });
});
}
}
Most of this file ends up just loading and initializing the libraries that we’re using. There are a few things to note though:
- First, we create a field reference for the
Api.express
property, and tell Flow that it will be an object of typeexpress$Application
from Express. - The constructor initializes an instance of Express, and attaches it to the instance of
Api
. Then it calls the other two methods,Api.middleware
andApi.routes
. Api.middleware
- Initializes and attaches our middleware modules to the app.Api.routes
- Right now, it attaches a single route handler that returns some JSON. However, notice the Flow annotations on the parameters of the anonymous function. These correspond to the base arguments for an Express route handler:$Request
and$Response
. These refer to Express’ extended versions of Node’sIncomingMessage
andServerResponse
objects, respectively.
At this point, you may start to see a Flow error in your terminal that looks something like this:
src/index.js:12
12: const server: Server = http.createServer(app.express);
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ call of method `createServer`
835: requestListener?: (request: IncomingMessage, response: ServerResponse) => void
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function type. Callable signature not found in. See lib: /private/tmp/flow/flowlib_120ceaae/node.js:835
12: const server: Server = http.createServer(app.express);
^^^^^^^^^^^ express$Application
It would appear that Flow doesn’t get the memo that when app.express
is called, it does return a request handler. This seems to be an issue with the libdef for Express, because it declares that the express$Application
constructor has a return type of void
.
NOTE: After unsuccessfully messing with the libdef for a while, I decided I knew that it worked better than Flow, and moved on. If the terminal output bugs you, go ahead and add this comment to the line above where
http.createServer
is called:// $FlowFixMe: express libdef issue
Let’s go ahead and fire up the app and make sure everything is working as intended thus far. To run the app from the command line, you can run node build/index.js
. However, we really should have a start script so we can just type npm start
to run the server. Open up package.json
and add the following:
"scripts": {
"start": "DEBUG=\"flow-api:*\" node build/index.js"
}
The first part of the command just sets the DEBUG
environment variable to flow-api:*
, so that the debug
module writes our logs to stdout. Now you can run npm start
, and you should see:
> DEBUG="flow-api:*" node build/index.js
flow-api:startup Listening on port 3000 +0ms
Awesome! The server is listening. Now, if we hit any endpoint, it should send back our { message: "Hello Flow!" }
payload. You can use httpie for this kind of thing. If you’re on a Mac you can install it with Homebrew: brew install httpie
. Then within a new terminal window run:
$ http localhost:3000/
And you should see:
→ HTTP/1.1 200 OK
Connection: keep-alive
Content-Length: 25
Content-Type: application/json; charset=utf-8
{
"message": "Hello Flow!"
}
And we’re up and running! At this point, we’ve got the base Express application up and running. Now we just need to build out a router that does something useful!
Test Setup
Not so fast! Rather than jumping straight into the RESTful router, we’re going to set up our testing environment so that as we create endpoints and handlers we can test that they work as we expect. Since we’re using the FaceStack, we’ll use Jest as well as supertest-as-promised to interface with our Express API.
Install the packages:
$ yarn add --dev<EMAIL_ADDRESS><EMAIL_ADDRESS><EMAIL_ADDRESS>Open up package.json again and add a few lines to configure Jest:
"jest": {
"transform": {".*": "<rootDir>/node_modules/babel-jest"}
}
This just tells Jest to use Babel and our Babel configuration to interpret our test files and the files they test. To run our tests from the command line, we just need to add a test script to package.json
:
"scripts": {
"start": "DEBUG=\"flow-api:*\" node build/index.js",
"test": "jest"
}
Right now, if you run it, Jest is just going to tell you it couldn’t find any tests So, let’s fix that. Create a directory called __tests__ in the project root, and inside of it add a file to hold our first test:
$ mkdir __tests__ && touch __tests__/first.test.js
import request from 'supertest-as-promised';
import Api from '../src/Api';
const app = new Api().express;
describe('Flow API', () => {
it('hello test', () => {
return request(app).get('/')
.expect(200)
.then((res) => {
expect(typeof res.body.message).toBe('string');
expect(res.body.message).toBe('Hello Flow!');
});
});
});
This is a pretty simple test, but it should at least demonstrate the basic structure of what we’re doing here. If you’re saying to yourself, “Hey, this looks a lot like Jasmine!”, you’re right it does, because Jest is built on top of Jasmine. Here’s a quick breakdown of this first test file:
- We import the
Api
class andsupertest-as-promised
to create the interface to the API. This way we don’t have to manage starting and stopping the server or actually sending requests over a network connection. - We assert that we’re expecting a 200 status code.
- When the response comes back, we assert that the payload should have a property called
message
, who’s value is a string, and that string should equal: “Hello Flow!”
Go ahead and run the tests, npm test
, and you should see this output:
> jest
PASS __tests__/first.test.js
Flow API
✓ hello test (42ms)
Test Suites: 1 passed, 1 total
Tests: 1 passed, 1 total
Snapshots: 0 total
Time: 2.03s
Ran all test suites.
GET / 200 4.059 ms - 25
With the test environment set up, let’s build out our first endpoint!
First Endpoint
Now, we’re going to implement CRUD with a single resource - produce. You can use any resource you want or grab the fake data we used here. In case you’re blanking on what CRUD means, we’re going to implement 4 actions that the API will support for the produce resource:
- Create a produce item.
- Read produce item(s).
- Update a produce item.
- Delete a produce item.
We’ll start by implementing the GET
handler that returns all the produce in our inventory, with the following shape:
{
id: integer,
name: string,
quantity: integer,
price: integer
}
NOTE: The
id
property will not be supplied by the user, but assigned when an item is created by the API.
Let’s start by first writing some tests that we can test our implementation against as we write it. Rename first.test.js to ProduceRouter.test.js, and replace the current describe
block with these tests for the GET
all endpoint:
describe('Flow API', () => {
describe('GET /api/v1/produce - get all produce', () => {
// properties expected on an obj in the response
let expectedProps = ['id', 'name', 'quantity', 'price'];
it('should return JSON array', () => {
return request(app).get('/api/v1/produce')
.expect(200)
.then(res => {
// check that it sends back an array
expect(res.body).toBeInstanceOf(Array);
});
});
it('should return objs w/ correct props', () => {
return request(app).get('/api/v1/produce')
.expect(200)
.then(res => {
// check for the expected properties
let sampleKeys = Object.keys(res.body[0]);
expectedProps.forEach((key) => {
expect(sampleKeys.includes(key)).toBe(true);
});
});
});
it('shouldn\'t return objs w/ extra props', () => {
return request(app).get('/api/v1/produce')
.expect(200)
.then(res => {
// check for only expected properties
let extraProps = Object.keys(res.body[0]).filter((key) => {
return !expectedProps.includes(key);
});
expect(extraProps.length).toBe(0);
});
});
});
});
Inside of the outer describe
, we’ve added a nested block to indicate that all of the tests inside of it are related and, thus, testing the same feature. These three tests are pretty basic and check that:
- We get an array back.
- The objects in the array have the required properties.
- The objects in the array do not have extra properties.
Run the tests from the terminal with npm test
and you should see them all fail:
Flow API
GET /api/v1/produce - get all produce
✕ should return JSON array (42ms)
✕ should return objs w/ correct props (9ms)
✕ shouldn't return objs w/ extra props (4ms)
Test Suites: 1 failed, 1 total
Tests: 3 failed, 3 total
Snapshots: 0 total
Time: 1.835s, estimated 2s
Ran all test suites.
Now, let’s get rid of all those errors and failed tests, and implement the endpoint.
Create a new directory inside of “src” called “routers” and add a file called ProduceRouter.js. This is where we’ll implement the handler functions for all of the endpoints designated for the produce resource.
NOTE: Remember - For Flow to type check the file, you have to add the
@flow
comment at the very top of the file!
// @ flow
import inventory from '../../data/produce';
import { Router } from 'express';
export default class ProduceRouter {
// these fields must be type annotated, or Flow will complain!
router: Router;
path: string;
// take the mount path as the constructor argument
constructor(path = '/api/v1/produce') {
// instantiate the express.Router
this.router = Router();
this.path = path;
// glue it all together
this.init();
}
/**
* Return all items in the inventory
*/
getAll(req: $Request, res: $Response): void {
res.status(200).json(inventory);
}
/**
* Attach route handlers to their endpoints.
*/
init(): void {
this.router.get('/', this.getAll);
}
}
The ProduceRouter
holds fields for an Express Router
instance, and a path
property that holds its mount point to the application. The constructor takes this mount point as its only argument and then attaches the endpoint handlers to their endpoints.
NOTE: The field type annotations for
router
andpath
are not strictly required (as far as I can tell). You can get rid of them, and Flow will not complain. But you can’t have field declarations without types. It doesn’t like that at all. I tend to use them because they’re a useful quick reference to the properties on an object.
The getAll
function has the basic function signature of an Express route handler, and it simply responds to requests with the full inventory list. Notice that the return type is void
. This is because of the middleware architecture that Express is built on. Each middleware function is run in sequence, rather than returning a value from the handler.
Finally, in init
we will take each of our route handlers, and attach it to a mount path on the router. Each endpoint will be prefixed with the overall Router
mount path that is passed to the ProduceRouter
constructor. Right now, our ProduceRouter
is responding to GET
requests at the /api/v1/produce
endpoint.
We’re done in this file for now, but we’ll have to hop back over to Api.js
in order to finish linking these things up.
Add an import statement for ProduceRouter
at the top:
import ProduceRouter from './routers/ProduceRouter';
And then replace the routes
function with:
// connect resource routers
routes(): void {
// create an instance of ProduceRouter
const produceRouter = new ProduceRouter();
// attach it to our express app
this.express.use(produceRouter.path, produceRouter.router);
}
Here, we simply create an instance of the ProduceRouter
class, and attach it to the Express application path specified by its path
property. Now cross your fingers and run npm test
:
PASS __tests__/ProduceRouter.test.js
Flow API
GET /api/v1/produce - get all produce
✓ should return JSON array (43ms)
✓ should return objs w/ correct props (10ms)
✓ shouldn't return objs w/ extra props (4ms)
Test Suites: 1 passed, 1 total
Tests: 3 passed, 3 total
Snapshots: 0 total
Time: 2.052s
Ran all test suites.
Victory! Go ahead and pat yourself on the back, maybe stretch the legs or get a snack. We’ll work out the rest of the endpoints in the next section.
Rounding out CRUD
We’ve already got one aspect of the “Read” part of CRUD complete. Let’s knock the other one out now. Rather than only being able to get the full list of items, we need to enable requesting items by their id
s. First, we need some tests. Start by making sure that this getById
handler will:
- Return an object of the correct type.
- Return the record that lines up with the
id
sent with the request. - Reject out-of-bounds
id
s.
describe('GET /api/v1/produce/:id - get produce item by id', () => {
it('should return an obj of type Produce', () => {
return request(app).get('/api/v1/produce/1')
.expect(200)
.then((res) => {
const reqKeys = ['id', 'name', 'price', 'quantity'];
const {item} = res.body;
// check it has correct keys
reqKeys.forEach((key) => {
expect(Object.keys(item)).toContain(key);
});
// check type of each field
expect(typeof item.id).toBe('number');
expect(typeof item.name).toBe('string');
expect(typeof item.quantity).toBe('number');
expect(typeof item.price).toBe('number');
});
});
it('should return a Produce w/ requested id', () => {
return request(app).get('/api/v1/produce/1')
.expect(200)
.then((res) => {
expect(res.body.item).toEqual({
id: 1,
name: 'banana',
quantity: 15,
price: 1
});
});
});
it('should 400 on a request for a nonexistant id', () => {
return Promise.all([
request(app).get('/api/v1/produce/-32')
.expect(400)
.then((res) => {
expect(res.body.message).toBe('No item found with id: -32');
}),
request(app).get('/api/v1/produce/99999')
.expect(400)
.then((res) => {
expect(res.body.message).toBe('No item found with id: 99999');
})
]);
});
});
Run those new tests and make sure they fail like they should:
Flow API
GET /api/v1/produce - get all produce
✓ should return JSON array (38ms)
✓ should return objs w/ correct props (8ms)
✓ shouldn't return objs w/ extra props (5ms)
GET /api/v1/produce/:id - get produce item by id
✕ should return an obj of type Produce (6ms)
✕ should return a Produce w/ requested id (2ms)
✕ should 400 on a request for a nonexistant id (9ms)
Test Suites: 1 failed, 1 total
Tests: 3 failed, 3 passed, 6 total
Snapshots: 0 total
Time: 1.857s, estimated 2s
Ran all test suites.
Good. Now we can work on making them pass. This one isn’t so bad. We just need to parse the ID number from the request params, and find an item in the inventory
array with the same ID.
/**
* Return an item from the inventory by ID.
*/
getById(req: $Request, res: $Response): void {
const id = parseInt(req.params.id, 10);
const record = inventory.find(item => item.id === id);
if (record) {
res.status(200).json({
message: 'Success!',
item: record
});
} else {
res.status(400).json({
status: res.status,
message: `No item found with id: ${id}`
});
}
}
Not particularly exciting, but it works for now! Just make sure to add the handler as well:
this.router.get('/:id', this.getById);
That does it for the “R” in CRUD.
POST - Create a New Item
Let’s knock out the “C” now. We’re going to allow POST
s to the endpoint /api/v1/produce
to be used for creating new items for the inventory. In addition, we’ll require that the quantity
, price
, and name
properties are passed.
Tests:
describe('POST /api/v1/produce - create new item', () => {
let peach = {
name: 'peach',
quantity: 10,
price: 6
};
it('should accept and add a valid new item', () => {
return request(app).post('/api/v1/produce')
.send(peach)
.then((res) => {
expect(res.body.status).toBe(200);
expect(res.body.message).toBe('Success!');
return request(app).get('/api/v1/produce');
})
.then((res) => {
let returnedPeach = res.body.find(item => item.name === 'peach');
expect(res.status).toBe(200);
expect(returnedPeach.quantity).toBe(10);
expect(returnedPeach.price).toBe(6);
});
});
it('should reject post w/o name, price, or quantity', () => {
let badItems = [
{
name: peach.name,
quantity: peach.quantity
},
{
quantity: peach.quantity,
price: peach.price
},
{
name: peach.name,
price: peach.price
}
];
return Promise.all(badItems.map(badItem => {
return request(app).post('/api/v1/produce')
.send(badItem)
.then((res) => {
expect(res.body.status).toBe(400);
expect(res.body.message.startsWith('Bad Request')).toBe(true);
});
}));
});
});
Verify that the tests fail with npm test
, then add another method to ProduceRouter
called postOne
.
NOTE: I ended up also writing functions to parse the payload from the request, as well as one to re-write our JSON “database” file. You can either include those as helper methods somewhere in the same file as
ProduceRouter
, or define them in a different file and import it. If you decide to import it, make sure that you type annotate the function so that Flow can work with its types. I chose to define them in different files and export them from there.
/**
* Add a new item to the inventory.
*/
postOne(req: $Request, res: $Response): void {
const received: Produce | boolean = parseProduce(req.body);
const newProduce = (received) ? req.body : null;
if (received) {
newProduce.id = genId(received, inventory);
inventory.push(newProduce);
res.status(200).json({
status: 200,
message: 'Success!',
item: newProduce
});
// write updated inventory to the file
saveInventory(inventory)
.then((writePath) => {
logger(`Inventory updated. Written to:\n\t${path.relative(path.join(__dirname, '..', '..'), writePath)}`);
})
.catch((err) => {
logger('Error writing to inventory file.');
logger(err.stack);
});
} else {
res.status(400).json({
status: 400,
message: 'Bad Request. Make sure that you submit an item with a name, quantity, and price.'
});
logger('Malformed POST to /produce.');
}
}
Create a new folder within “src” called “util”. Then add a parsers.js file:
export function parseProduce(input: any): boolean {
const requirements = [
{ key: 'name', type: 'string' },
{ key: 'quantity', type: 'number' },
{ key: 'price', type: 'number' }
];
return requirements.every((req) => {
return input.hasOwnProperty(req.key) && typeof input[req.key] === req.type;
});
}
…and save.js:
// @flow
import path from 'path';
import fs from 'fs';
// use a Flow type import to get our Produce type
import type {Produce} from './types';
export default function saveInventory(inventory: Array<Produce>): Promise<string> {
let outpath = path.join(__dirname, '..', '..', 'data', 'produce.json');
return new Promise((resolve, reject) => {
// lets not write to the file if we're running tests
if (process.env.NODE_ENV !== 'test') {
fs.writeFile(outpath, JSON.stringify(inventory, null, '\t'), (err) => {
(err) ? reject(err) : resolve(outpath);
});
}
});
}
export function genId(prod: Produce, inv: Array<Produce>): number {
let maxId: number | typeof undefined = inv[0].id;
inv.slice(1).forEach((item) => {
if (item.id && item.id > maxId) maxId = item.id;
});
return maxId + 1;
}
We don’t have tests written currently for these, but they’re pretty simple functions. Most importantly, now we have a couple utility functions that we can reuse. We’ll definitely need to reuse saveInventory
whenever we need to persist changes to the JSON file holding the inventory.
Add the imports to ProduceRouter.js:
import saveInventory, {genId} from '../util/save';
import { parseProduce } from '../util/parsers';
Then update the init()
:
/**
* Attach route handlers to their endpoints.
*/
init(): void {
this.router.get('/', this.getAll);
this.router.get('/:id', this.getById);
this.router.post('/', this.postOne);
}
With this code filled in, run npm test
again and when you’ve got all green check marks, head on to the next section.
PUT - Update an Item
This route will allow requests to update the properties of a single item. We need to make sure that a user is unable to change the id
property of the item so that they can’t create collisions. To solve this issue, we need to strip out all invalid keys from the submitted payload. But first, a few tests:
describe('PUT /api/v1/produce/:id - update an item', () => {
it('allows updates to props other than id', () => {
return request(app).put('/api/v1/produce/1')
.send({ quantity: 20 })
.then((res) => {
expect(res.status).toBe(200);
expect(res.body.message).toBe('Success!');
expect(res.body.item.quantity).toBe(20);
});
});
it('rejects updates to id prop', () => {
return request(app).put('/api/v1/produce/1')
.send({ id: 10 })
.then((res) => {
expect(res.status).toBe(400);
expect(res.body.message.startsWith('Update failed')).toBe(true);
});
});
});
Add the new handler to ProduceRouter
:
/**
* Update a Produce item by id.
*/
updateOneById(req: $Request, res: $Response): void {
const searchId: number | boolean = parseId(req.params);
const payload: any = parseUpdate(req.body);
let toUpdate: Produce = inventory.find(item => item.id === searchId);
if (toUpdate && payload) {
Object.keys(payload).forEach((key) => {
if (key === 'quantity' || key === 'price') toUpdate[key] = Number(payload[key]);
else toUpdate[key] = payload[key];
});
res.json({
status: res.status,
message: 'Success!',
item: toUpdate
});
saveInventory(inventory)
.then((writePath) => {
logger(`Item updated. Inventory written to:\n\t${path.relative(path.join(__dirname, '..', '..'), writePath)}`);
})
.catch((err) => {
logger('Error writing to inventory file.');
logger(err.stack);
});
} else {
res.status(400).json({
status: res.status,
message: 'Update failed. Make sure the item ID and submitted fields are correct.'
});
}
}
Then, within parsers.js, add the parseId()
and parseUpdate()
helpers, which are used to clean the payload and requested item ID:
export function parseUpdate(input: any): any | null {
const validKeys = ['name', 'quantity', 'price'];
const trimmed = Object.keys(input).reduce((obj, curr) => {
if (obj && validKeys.indexOf(curr) !== -1) {
obj[curr] = input[curr];
return obj;
}
}, {});
return (trimmed && Object.keys(trimmed).length > 0) ? trimmed : null;
}
export function parseId(input: any): number | boolean {
if (input.hasOwnProperty('id'))
return (typeof input.id === 'string') ? parseInt(input.id, 10) : input.id;
return false;
}
These are fairly straightforward. parseUpdate
takes in the payload from the request, and strips out any keys that are not name
, quantity
, or price
. Then it just simply returns the trimmed object if there’s still keys left, and null
if not. parseId
is even simpler: It looks for an id
property on the payload, converts it to a number (if necessary), and returns.
Update the import in ProduceRouter.js:
import { parseProduce, parseUpdate, parseId } from '../util/parsers';
Then update the init()
:
/**
* Attach route handlers to their endpoints.
*/
init(): void {
this.router.get('/', this.getAll);
this.router.get('/:id', this.getById);
this.router.post('/', this.postOne);
this.router.put('/:id', this.updateOneById);
}
Run the tests again and ensure they pass. One more route to go!
DELETE - Remove an Item
This route will allow for deleting an item from the inventory by passing a valid id
as a URL parameter. This is the same string route that the getById
and updateOneById
functions handle, but will use the DELETE
HTTP method. Here’s a few basic tests:
describe('DELETE /api/v1/produce/:id - delete an item', () => {
it('deletes when given a valid ID', () => {
return request(app).delete('/api/v1/produce/4')
.then((res) => {
expect(res.status).toBe(200);
expect(res.body.message).toBe('Success!');
expect(res.body.deleted.id).toBe(4);
});
});
it('responds w/ error if given invalid ID', () => {
return Promise.all([-2, 100].map((id) => {
return request(app).delete(`/api/v1/produce/${id}`)
.then((res) => {
expect(res.status).toBe(400);
expect(res.body.message).toBe('No item found with given ID.');
});
}));
});
});
Ensure those fail, and then add the implementation for the handler to ProduceRouter
as removeById
:
/**
* Remove an item from the inventory by ID.
*/
removeById(req: $Request, res: $Response): void {
const searchId: number | boolean = parseId(req.params);
let toDel: number = inventory.findIndex(item => item.id === searchId);
if (toDel !== -1) {
let deleted = inventory.splice(toDel, 1)[0];
res.json({
status: 200,
message: 'Success!',
deleted
});
// update json file
saveInventory(inventory)
.then((writePath) => {
logger(`Item deleted. Inventory written to:\n\t${writePath}`);
})
.catch((err) => {
logger('Error writing to inventory file.');
logger(err.stack);
});
} else {
res.status(400).json({
status: 400,
message: 'No item found with given ID.'
});
}
}
This obviously looks pretty similar to most of the other handlers. The only difference being that once we get a valid id
, we search for the object it matches in the inventory, get its index, and then splice it out of the inventory array.
Don’t forget the handler:
this.router.delete('/:id', this.removeById);
Run the tests one last time:
PASS __tests__/ProduceRouter.test.js
Flow API
✓ allows updates to props other than id (4ms)
GET /api/v1/produce - get all produce
✓ should return JSON array (50ms)
✓ should return objs w/ correct props (11ms)
✓ shouldn't return objs w/ extra props (18ms)
GET /api/v1/produce/:id - get produce item by id
✓ should return an obj of type Produce (8ms)
✓ should return a Produce w/ requested id (6ms)
✓ should 400 on a request for a nonexistant id (9ms)
POST /api/v1/produce - create new item
✓ should accept and add a valid new item (27ms)
✓ should reject post w/o name, price, or quantity (9ms)
PUT /api/v1/produce/:id - update an item
✓ allows updates to props other than id (6ms)
✓ rejects updates to id prop (7ms)
DELETE /api/v1/produce/:id - delete an item
✓ deletes when given a valid ID (4ms)
✓ responds w/ error if given invalid ID (11ms)
Test Suites: 1 passed, 1 total
Tests: 13 passed, 13 total
Snapshots: 0 total
Time: 2.322s
Ran all test suites.
Congratulations! You just built an Express API type checked with Flow!
Conclusion
All in all, working with Flow is interesting, at the very least.
After using both it and TypeScript, Flow’s type checking tends to be more strict, but you also spend more time trying to figure out what Flow is getting at and how to fix errors. Part of this is probably that the tooling support for TypeScript is vastly superior. Flow offers a lot of the same functionality that TypeScript does, but there’s a TypeScript tool for every single thing you could ever want. It simply isn’t the same for Flow. The community doesn’t seem to have embraced it with as much enthusiasm. The number of libdefs in the flow-typed
repository versus DefinitelyTyped
for TypeScript is tiny. This is probably the biggest problem you’d have to face in choosing to use Flow for static type analysis over TypeScript.
That being said, Flow also offers some distinct advantages.
It’s plug-n-play with Babel, so adding Flow to a project using Babel would probably be much less painful than converting it to use TypeScript. Both allow you to do so bit by bit, but Flow handles this more gracefully. TypeScript would usually like to just have you pass everything through the compiler and deal with the type errors as you can. Flow allows you to annotate only the files you want to type check, so adding it to an existing project is much easier. Actually, this is probably the best use case for Flow. It would be cumbersome to start a brand new project with such strict type checking. It definitely slows down the rapid iteration needed at the beginning of a project’s life. However, once the project gets to a certain size it’s easy to drop in Flow and clean up the errors file by file as you move forward.
You can grab the code from the flow-node-api repo. Best!


GHANIAN NAME
Ghanian Name is a website that is going to takes a user's birthday and calculates the day of the week
you were born and depending on your gender it gives you a Ghanian Name
derived from Ghanian culture.
Male
- Sunday: Kwasi
- Monday: Kwadwo
- Tuesday: Kwabena
- Wednesday: Kwaku
- Thursday: Yaw
- Friday: Kofi
- Saturday: Kwame
Female
- Sunday: Akosua
- Monday: Adwoa
- Tuesday: Abenaa
- Wednesday: : Akua
- Thursday: Yaa
- Friday: Afua
- Saturday: Ama
Fill out the form and get to know your Ghanian name!


Welcome to Sleep
So what is Sleep? The short answer is that it comes from the combination of Netflix and food, something we hope we're doing constantly. A slightly longer version is that we're a collection of foodgraphers, Netflixers, and creatives who enjoy urban food. During our travels around the country, we take time to document the cities we love. You'll also occasionally find articles on new dishes, our thoughts on the latest shows and movies, and the odd posting on sleep.
Play free games at Kongregate
So... what aren't we?
Professional photographers, for one. Please keep in mind that we're strictly amateur foodgraphers. We submit these galleries out of love and creative expression. We're not likely to remember the temperature we cook our food at, or even what ingridients we had. We're also not very exclusive. If you like what you see here, and want to participate, we'd love to have you. All galleries, recipes, and posts go through a submittal and approval process, so if you're interested, drop us a line and tell us what you have in mind.
We also occasionally have meetups. If, for example, we know we're coming to Japan, we'll let everyone know and schedule a meet up day to tour the country and eat food. If you'd like us to visit your country, let us know!
Upcoming cities
Want to be a part of a sleep.org meetup? Check out our upcoming dates and contact us if you'd like to hang out and watch some Netflix with us!
- Marseille Jan 03
- New York City Jan 23
- Syndey March 05
- Seoul April 13
- Los Angeles June 03
- Seattle July 16
Foodgraphers rights
Due to the heightened security concerns around the food, it has become common to find foodgraphers routinely challenged when taking food in public places. With that in mind, we invite you to join us in a free forum as noted foodgrapher Shea Hansen presents "Foodgrapher's Rights in a Post 9/11 World."
Shea will discuss what your rights are when taking food in a public space. Following her talk we'll have an open forum where you can ask questions, compare experiences, and become better informed as to what your responsibilities are as amateur foodgraphers.
The forum will be held at thefunnel-cake Gallery, 227 West 29th Street in Manhattan on March 19th as part of their "Foodgage" lecture series.


Please note that SIMK is still in development and some functionality would not work without an internet connection If the app doesn't load beyond here Report here


View Larger Map
Kings Court English College House (KCECH) is an undergraduate dormitory on the campus of the University of Pennsylvania. It's address is 3465 Sansom St, Philadelphia PA, 19104.
There is currently a lot of construction on Sansom St, so it may be difficult to see the entrance way to KCECH from afar. The entrance is between 36th and 34th Street on Sansom, and it is closer to 36th than it is to 34th.
Since it is a Penn dorm, non-resident Penn students need their Penn Cards to gain access to the building. Non-resident Penn students without a Penn Card and non-Penn students need a photo ID and must be signed in by a resident to gain access to the building.


public class Terminal extends java.lang.Object implements ComponentObserver
Component
s on the screen. Components
which are interactive may use this class to receive input from the mouse
and keyboard.
This is not a real terminal, and does not expose the underlying system's command interface. The aim of this class is to present Components as if they are being displayed in a terminal. It should be noted then that an application started from the system command line will open a new window in which it will be displayed.
Before Components can be added to an instance of this class, the terminal
must be initialized with a Layout
for the RootContainer
.
This RootContainer is a Container at the top of the Component hierarchy in
the terminal (meaning that all Components displayed in a terminal window are
an ancestor of this Container in the Component tree). A typical
instantiation of a terminal might look something like this:
Terminal terminal = new Terminal.Builder() // ... .dimensions(40, 40) .build(); terminal.createRootContainer(new ALayoutOfSomeKind()); // Create some Components here... terminal.add(component1, component2, component3);Note that Components (including other Containers) can be added at any time to the interface, and this interface will update automatically. Nothing needs to be done on the client-side, aside from initializing the terminal, creating the Components, and adding them to the terminal (where they will be direct children of the RootContainer) or adding them to another Container which has been added to the terminal.
| Modifier and Type | Class and Description |
|---|---|
static class |
Terminal.Builder
Aids in the construction of new instance of a
Terminal . |
| Modifier and Type | Field and Description |
|---|---|
protected Context |
context
Holds general information regarding the settings of this instance of the
terminal.
|
private EventDispatcher |
dispatcher
Handles all events that may occur at random (and unpredictable) times
within the terminal.
|
protected KeyboardTarget |
focused
The current component receiving key events.
|
private Prompt |
prompt
The prompt handles all user-input in the terminal.
|
private Renderer |
renderer
Uses the given render settings to rasterize and display frames in the
window.
|
protected RootContainer |
root
The root container that all components that appear in the terminal
belong to.
|
protected javax.swing.JFrame |
window
The window for displaying the buffer to the screen.
|
| Constructor and Description |
|---|
Terminal(Context context,
java.lang.String title,
java.awt.Color bg,
float trans,
Renderer.RasterType rasterType)
Creates a new instance of
Terminal based on the given
Context 's properties. |
| Modifier and Type | Method and Description |
|---|---|
void |
add(Component... components)
Adds the components to the root container.
|
protected void |
clickComponent(Location location) |
RootContainer |
createRootContainer(Layout layout)
Constructs and returns a new
RootContainer using this terminal
as the context, and the given layout as the super layout of all
Component s within the terminal. |
void |
focus(KeyboardTarget component)
Sets the given component as the target for key events.
|
void |
focusAt(Location location)
Focuses the component at the given location, and makes it the target for
key events.
|
(package private) int |
getCharHeight() |
(package private) int |
getCharWidth() |
Context |
getContext() |
KeyboardTarget |
getFocusedComponent() |
RootContainer |
getRoot()
Gets the root container, which is the parent of all components within
the terminal.
|
java.lang.String |
requestInput(java.lang.String msg)
Allows for a user to input text into the prompt.
|
void |
update()
Redraws all
Component s within the root container to a new frame,
and then renders that frame to the screen. |
protected Context context
protected javax.swing.JFrame window
BufferedFrame
private Renderer renderer
private Prompt prompt
Terminal
.requestInput(String)
protected RootContainer root
add(Component[])
private EventDispatcher dispatcher
protected KeyboardTarget focused
focus(KeyboardTarget)
,
focusAt(Location)
public Terminal(Context context, java.lang.String title, java.awt.Color bg, float trans, Renderer.RasterType rasterType)
Terminal
based on the given
Context
's properties.context
- The setting information for the terminal.title
- The title of the terminal window.bg
- The color of the bg which appears being the
text.trans
- A normalized value between zero and one, which will
determine how much of the screen is visible behind
the window, as well as the visibility of the text.rasterType
- The type of rasterizer to use for rendering text to
the window. This should not make much noticeable
difference in appearance, though it may impact
performance of an application.public Context getContext()
public RootContainer createRootContainer(Layout layout)
RootContainer
using this terminal
as the context, and the given layout as the super layout of all
Component
s within the terminal.layout
- The layout to use for placing Components within this
terminal.RootContainer
which has been constructed for this
terminal with the given layout.public RootContainer getRoot()
int getCharWidth()
int getCharHeight()
public void add(Component... components)
components
- The components to add to the root container.public void focus(KeyboardTarget component)
component
- The component to focus at.public void focusAt(Location location)
location
- The location to focus at.public KeyboardTarget getFocusedComponent()
protected void clickComponent(Location location)
public java.lang.String requestInput(java.lang.String msg)
msg
- The message to show to the user in the prompt. This message
will be followed by a separator character, after which the
editable part of the prompt will follow.public void update()
Component
s within the root container to a new frame,
and then renders that frame to the screen.update
in interface ComponentObserver


JavaScript Reserved Words
You should avoid using these reserved words and keywords as function or variable names as JavaScript has reserved these words for its own use.
| JavaScript Reserved Words |
| break |
export |
return |
| case |
for |
switch |
| comment |
function |
this |
| continue |
if |
typeof |
| default |
import |
var |
| delete |
in |
void |
| do |
label |
while |
| else |
new |
with |
| Java Keywords (Reserved by JavaScript) |
| abstract |
implements |
protected |
| boolean |
instanceOf |
public |
| byte |
int |
short |
| char |
interface |
static |
| double |
long |
synchronized |
| false |
native |
throws |
| final |
null |
transient |
| float |
package |
true |
| goto |
private |
|
| ECMAScipt Reserved Words |
| catch |
enum |
throw |
| class |
extends |
try |
| const |
finally |
|
| debugger |
super |
|
| Other JavaScript Keywords |
| alert |
isFinite |
personalbar |
| Anchor |
isNan |
Plugin |
| Area |
java |
print |
| arguments |
JavaArray |
prompt |
| Array |
JavaClass |
prototype |
| assign |
JavaObject |
Radio |
| blur |
JavaPackage |
ref |
| Boolean |
length |
RegExp |
| Button |
Link |
releaseEvents |
| callee |
location |
Reset |
| caller |
Location |
resizeBy |
| captureEvents |
locationbar |
resizeTo |
| Checkbox |
Math |
routeEvent |
| clearInterval |
menubar |
scroll |
| clearTimeout |
MimeType |
scrollbars |
| close |
moveBy |
scrollBy |
| closed |
moveTo |
scrollTo |
| confirm |
name |
Select |
| constructor |
NaN |
self |
| Date |
navigate |
setInterval |
| defaultStatus |
navigator |
setTimeout |
| document |
Navigator |
status |
| Document |
netscape |
statusbar |
| Element |
Number |
stop |
| escape |
Object |
String |
| eval |
onBlur |
Submit |
| FileUpload |
onError |
sun |
| find |
onFocus |
taint |
| focus |
onLoad |
Text |
| Form |
onUnload |
Textarea |
| Frame |
open |
toolbar |
| Frames |
opener |
top |
| Function |
Option |
toString |
| getClass |
outerHeight |
unescape |
| Hidden |
OuterWidth |
untaint |
| history |
Packages |
unwatch |
| History |
pageXoffset |
valueOf |
| home |
pageYoffset |
watch |
| Image |
parent |
window |
| Infinity |
parseFloat |
Window |
| InnerHeight |
parseInt |
|
| InnerWidth |
Password |
|


| File | Statements | Branches | Functions | Lines | |||||
|---|---|---|---|---|---|---|---|---|---|
| CLI.js | 51.2% | 64/125 | 32.14% | 18/56 | 72.73% | 8/11 | 51.61% | 64/124 | |
| Config.js | 100% | 8/8 | 100% | 2/2 | 100% | 2/2 | 100% | 7/7 | |
| Error.js | 100% | 8/8 | 100% | 0/0 | 100% | 1/1 | 100% | 8/8 | |
| PluginManager.js | 98.44% | 63/64 | 90.48% | 19/21 | 100% | 11/11 | 98.39% | 61/62 | |
| Service.js | 96.43% | 81/84 | 95% | 38/40 | 100% | 13/13 | 97.5% | 78/80 | |
| Utils.js | 88.64% | 78/88 | 78.95% | 30/38 | 100% | 11/11 | 97.4% | 75/77 | |
| YamlParser.js | 100% | 14/14 | 100% | 0/0 | 100% | 2/2 | 100% | 13/13 |


Profile
Complete your profile
CEO / Co-Founder
Alec Thompson
Don't be scared of the truth because we need to restart the human foundation in truth And I love you like Kanye loves Kanye I love Rick Owens’ bed design but the back is...
FollowComplete your profile
Don't be scared of the truth because we need to restart the human foundation in truth And I love you like Kanye loves Kanye I love Rick Owens’ bed design but the back is...
Follow


ScrollTrigger
Add scroll based animations to your site with ease.
CSS Animations
All animations are triggered by classes, so you can take advantage of the power that CSS3 animations give you.
Vanilla JS
It's written in vanilla javascript, so you can use it with any framework.
Request Animation Frame
It uses the window.requestAnimationFrame method so it doesn't jack the users scroll. Animations are triggered when the browser is ready for it.
Simple syntax
You define the options inside the HTML attribute, so after you have initialised the ScrollTrigger you don't have to touch JavaScript again.
Customizable
ScrollTrigger offers a range of options, from calling custom JavaScript functions to defining custom offsets and classes.
You scrolled past this block 0 times.
Versatile
ScrollTrigger supports vertical and horizontal scrolling, check out the horizontal demo.
This block only animates once.
Open source
The project, with documentation, is on GitHub. Give it some love!


Create an account
FULL NAME
COUNTRY NAME
EMAIL ADDRESS
PHONE NUMBER
PASSWORD
CONFIRM PASSWORD
I AGREE PARENTAL VALUES TERM & CONDITIONS
SIGN UP
Alread have account ?
login here


Class EventBasedNetListener
Inheritance
EventBasedNetListener
Assembly: LiteNetLib.dll
Syntax
public class EventBasedNetListener : INetEventListener, IDeliveryEventListener, INtpEventListener, IPeerAddressChangedListener
Methods
ClearConnectionRequestEvent()
Declaration
public void ClearConnectionRequestEvent()
ClearDeliveryEvent()
Declaration
public void ClearDeliveryEvent()
ClearNetworkErrorEvent()
Declaration
public void ClearNetworkErrorEvent()
ClearNetworkLatencyUpdateEvent()
Declaration
public void ClearNetworkLatencyUpdateEvent()
ClearNetworkReceiveEvent()
Declaration
public void ClearNetworkReceiveEvent()
ClearNetworkReceiveUnconnectedEvent()
Declaration
public void ClearNetworkReceiveUnconnectedEvent()
ClearNtpResponseEvent()
Declaration
public void ClearNtpResponseEvent()
ClearPeerAddressChangedEvent()
Declaration
public void ClearPeerAddressChangedEvent()
ClearPeerConnectedEvent()
Declaration
public void ClearPeerConnectedEvent()
ClearPeerDisconnectedEvent()
Declaration
public void ClearPeerDisconnectedEvent()
Events
ConnectionRequestEvent
Declaration
public event EventBasedNetListener.OnConnectionRequest ConnectionRequestEvent
Event Type
DeliveryEvent
Declaration
public event EventBasedNetListener.OnDeliveryEvent DeliveryEvent
Event Type
NetworkErrorEvent
Declaration
public event EventBasedNetListener.OnNetworkError NetworkErrorEvent
Event Type
NetworkLatencyUpdateEvent
Declaration
public event EventBasedNetListener.OnNetworkLatencyUpdate NetworkLatencyUpdateEvent
Event Type
NetworkReceiveEvent
Declaration
public event EventBasedNetListener.OnNetworkReceive NetworkReceiveEvent
Event Type
NetworkReceiveUnconnectedEvent
Declaration
public event EventBasedNetListener.OnNetworkReceiveUnconnected NetworkReceiveUnconnectedEvent
Event Type
NtpResponseEvent
Declaration
public event EventBasedNetListener.OnNtpResponseEvent NtpResponseEvent
Event Type
PeerAddressChangedEvent
Declaration
public event EventBasedNetListener.OnPeerAddressChangedEvent PeerAddressChangedEvent
Event Type
PeerConnectedEvent
Declaration
public event EventBasedNetListener.OnPeerConnected PeerConnectedEvent
Event Type
PeerDisconnectedEvent
Declaration
public event EventBasedNetListener.OnPeerDisconnected PeerDisconnectedEvent
Event Type
Implements


Write jQuery plugins to create great looking user interactions. Find and implement already written jQery plugins for use in projects.


PROJECTS
I have done several projects of the past few years related to various fields, including Game Design, Software Development, Artificial Intelligence and Computer Vision. These projects were done mostly on my own, while some were a part of a larger team based project. I have gained a lot of experience and knowledge about various topics through these projects and continue to work on new ideas and concepts.
Hyper Casual Game, Game Design, Level Design
Computer Vision, Artificial Intelligence Application
Central repository of resources and events available to people with disabilities
Web based game with pokemon theme, React frontend, Express backend, MongoDB database
Android Software Development(Java), Game Design
Deep Learning, Artificial Intelligence Research Paper
Japanese - English Dictionary based on Genki Second Edition
Hyper Casual Game, Game Design, Level Design