{% extends 'base.html' %} {% block content %}
Created by Kevin, Sam, Justin
Using a public dataset, Keras to create a neural network and machine learning from Tensorflow, this program was create to recognizes the 2019 Novel Coronavirus (COVID-19) also known as the Severe Acute Respiratory Syndrome Coronavirus 2 (SARS-CoV-2) in X-ray images of human lungs. In hopes of further assisting with the battle with the global pandemic, this site was created for the users to determine if the images of lungs they have belong to an individual who are showing developement of COVID.
In hopes of a better tomorrow
{% endblock %}


THE PUPPET MASTERS A film review by Mark R. Leeper Copyright 1994 Mark R. Leeper
Capsule review: THE PUPPET MASTERS is a no-holds- barred alien invasion film. It is a dark and humorless exercise in which virtually every scene advances the war between humans and parasitic, mind-controlling aliens. Robert Heinlein's 1951 novel comes to the screen in a film that takes little time away from the main plot to develop its characters, but does strike home with a story of a hard-fought battle with a fairly alien and devious enemy. Rating: high +1 (-4 to +4)
The obvious comparison to make would be between THE PUPPET MASTERS and the three film versions of Jack Finney's BODY SNATCHERS. That would probably not be accurate, however, since this film is about the aliens possessing humans rather than passing for them. But the paranoid, trust-nobody spirit is much like that of those previous films.
An alien craft lands in the rural town of Ambrose, Iowa, and authorities rush in to investigate, only to find that the locals all now claim that the landing was a hoax. In this case the authorities are two agents of a scientific sub-agency of the CIA: Sam (played by Eric Thal), the head of the agency, and Andrew (Donald Sutherland). Also along is an expert borrowed from NASA, Mary (Julie Warner). They quickly determine that there is good reason to believe the landing was indeed authentic. And shortly afterward they also determine that humans are being mentally possessed by slug-like aliens who hide on humans' backs and tap into the nervous system to enslave the hosts. The number of people possessed in this way increases super- exponentially.
If one compares THE PUPPET MASTERS to any version of THE BODY SNATCHERS, one finds the emphasis very different, and, in fact, different from most other science fiction films. In the earlier films, the time is taken to establish who the characters are and to provide a context for their actions. This is a film in which things start happening in the first minute of the film and from that point on virtually every scene is devoted to the conflict. The people never stand around and talk about each other long enough for the script by Red Elliot, Terry Russo, and David Goyer to develop their character. This makes for rather flat characters but a very tense film. Their script also "downdates" the story, moving it into our present. Heinlien's 1951 novel set the story in a 2007 when interplanetary colonization was already well under way. That and perhaps budget constraints limit this invasion to just a first foothold in a limited area. Perhaps that is more realistic than the novel, but it also leaves open the unspoken possibility that if it came to a question of human survival, the alien menace could be ended with a few carefully placed nuclear weapons. A few other changes are made to keep the film consistent with its present or near-future setting.
Under Stuart Orme's direction THE PUPPET MASTERS is a film that is fact-paced and yet very dark and somber in tone, reminiscent of John Sturges's SATAN BUG. Donald Sutherland plays his role as the head of the security agency with a sinister dignity. As a man supposedly without emotion, he speaks always in hushed tones which only add to the tension. Eric Thal, who was Ariel in A STRANGER AMONG US, plays a character similarly dedicated to his purpose and similarly one- dimensional. It must be disheartening for Thal to be the film's ostenstible main character but to be outacted by ten-inch slugs from the special effects department. The slugs, incidentally, are sufficiently alien and repulsive as well as being reasonably convincing as effects. Julie Warner also is given more distress than personality by the script. The design of the film goes slightly downhill from a rather beautiful celestial event at the beginning to scenes of alien architecture that look a bit cheesey in spots.
THE PUPPET MASTERS is a nail-biter with some good moments, but will probably be easily over-shadowed by the upcoming FRANKENSTEIN and INTERVIEW WITH THE VAMPIRE. It gets a high +1 on the -4 to +4 scale.
Mark R. Leeper<EMAIL_ADDRESS>.
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


{% load staticfiles %}
Fantasy Football League Stats
Fantasy League Stats
Enter Your League Number Below
{% csrf_token %} {{ form.as_p }}
Click here for a demo
Where's my league number?
Website by
Ben Alex Keen


:: com :: sun :: star :: form :: binding ::
|
| unpublished |
service ValueBinding |
|
|
- Usage Restrictions
- not published
- Description
- defines a component which allows access to a single value
Read/Write access to the value represented by this component is supported,
as well as (optionally) active broadcasting of value changes
|
| Properties' Summary |
[ readonly bound ] boolean
ReadOnly |
[ OPTIONAL ]
determines whether the value is currently readonly
|
[ readonly bound ] boolean
Relevant |
[ OPTIONAL ]
determines the relevance of the value represented by the binding
|
| Exported Interfaces - Details |
::com::sun::star::beans::XPropertySet
- Usage Restrictions
- optional
- Description
- allows access to the properties of the binding
|
|
XValueBinding
- Description
- allows read and write access to the value represented by this binding
|
|
::com::sun::star::util::XModifyBroadcaster
- Usage Restrictions
- optional
- Description
- allows other components to be notified when the value represented
by the ValueBinding instance changes.
This interface is optional, since a binding may not support
actively notifying changes in it's value. Note, however, that in case
this interface is not supported, the bound component cannot react
on value changes, and will thus override any values which are
set by an instance other than itself.
|
|
::com::sun::star::lang::XComponent
- Description
- allows life time control for the component
An ValueBinding may be known to one ore more components
supporting the XBindableValue interface, which all work with
this binding. However, they will not own the ValueBinding.
The ownership is with another instance, which may also decide to obsolete
the ValueBinding for whatever reasons (e.g. because the data model
which the binding reflected died). For this reason, a ValueBinding
must offer a possibility to be obsoleted by it's owner, and to notify this
obsoletion to other interested parties, such as XBindableValues.
|
|
| Properties' Details |
ReadOnly
[ readonly bound ] boolean ReadOnly;
- Usage Restrictions
- optional
- Description
- determines whether the value is currently readonly
For instance, you could imagine a ValueBinding which
represents a cell in a spreadsheet document, and whose value is readonly
as long as the spreadsheet is locked.
As long as this property is true, the value binding should throw
a InvalidBindingStateException when its
XValueBinding::setValue method is invoked.
|
|
Relevant
[ readonly bound ] boolean Relevant;
- Usage Restrictions
- optional
- Description
- determines the relevance of the value represented by the binding
In a more complex scenario, where different form controls are bound to different
values, which all are part of a larger data structure, some of the items in this
data structure may not be relevant currently. This is indicated by the
Relevant property being false.
XBindableValues which are bound to this binding may or may not
react in certain ways on the (ir)relevance of their bound value.
One possible reaction could be that user interface elements which are associated
with the XBindableValue are disabled as long as Relevant
is false.
|
|
Top of Page
Copyright © 2013, The Apache Software Foundation, Licensed under the Apache License, Version 2.0. Apache, the Apache feather logo, Apache OpenOffice and OpenOffice.org are trademarks of The Apache Software Foundation. Other names may be trademarks of their respective owners.


Average score: {{list.progress}}%
Welcome to Grammarer!
What do you want to revise today?
Magic Dashboard for teachers and administrators
{{ lists.lists[0].language }}
{{list.title}}
Average score: {{list.progress}}%
{{ lists.vocab[0].language }}
{{ list.language }} | Checklist {{ list.number }}
Average score: {{ list.progress }}%
The following files are designed to be printed out and used to revise during computer-free study sessions.
gm-options.json
and re-run node setup
to change printables. PDF files are located in frontend/pdf
.
{{ pdf.language }} | {{ pdf.name }}
{{ pdf.fileName }}


Summary Incident in December 2017
Summary By Root Cause
- CR Side Impact
- Data Migration Issue
- Deployment Issue
- Design Issue
- Newly Used Function
- Program Bugs
Summary By Division
- Credit Analyst
- Finance
- General Affairs
- IT SPD
- Marketing
- Collection
- All
Chart Bar Incident in December 2017
Incident: 1 Des 2017 - 31 Des 2017
Monthly Recap Report
Incident: 1 Jan 2017 - 30 Jul 2017


In your system, a Spring Boot web application is deployed on your node "Node1".
This one is easy. Your app must always be available. No excuses.
If you like advanced stuff, think about what happens when you deploy your application. Do you have any downtime? Would you like some Blue/Green deployment?
Refer to the guide in your Starter Kit for more information about IP, SSH keys, etc.


{parse} = require 'url'
It works in all the browsers I've tried.
I've written this using the literate programming style to try it out. So, thats why there's a million comments everywhere.
The server is implemented as connect middleware. Its intended to be used like this:
server = connect(
browserChannel (client) -> client.send 'hi'
)
parse
helps us decode URLs in requests
{parse} = require 'url'
querystring
will help decode the URL-encoded forward channel data
querystring = require 'querystring'
fs
is used to read & serve the client library
fs = require 'fs'
Client sessions are EventEmitters
{EventEmitter} = require 'events'
Client session Ids are generated using node-hat
hat = require('hat').rack(40, 36)
randomInt(n)
generates and returns a random int smaller than n (0 <= k < n)
randomInt = (n) -> Math.floor(Math.random() * n)
randomArrayElement(array)
Selects and returns a random element from array
randomArrayElement = (array) -> array[randomInt(array.length)]
For testing we'll override setInterval
, etc with special testing stub versions (so
we don't have to actually wait for actual time. To do that, we need local variable
versions (I don't want to edit the global versions). ... and they'll just point to the
normal versions anyway.
{setInterval, clearInterval, setTimeout, clearTimeout, Date} = global
The module is configurable
defaultOptions =
An optional array of host prefixes. Each browserchannel client will randomly pick from the list of host prefixes when it connects. This reduces the impact of per-host connection limits.
All host prefixes should point to the same server. Ie, if your server's hostname is example.com and your hostPrefixes contains ['a', 'b', 'c'], a.example.com, b.example.com and c.example.com should all point to the same host as example.com.
hostPrefixes: null
You can specify the base URL which browserchannel connects to. Change this if you want to scope browserchannel in part of your app, or if you want /channel to mean something else, or whatever.
base: '/channel'
We'll send keepalives every so often to make sure the http connection isn't closed by eagar clients. The standard timeout is 30 seconds, so we'll default to sending them every 20 seconds or so.
keepAliveInterval: 20 * 1000
After awhile (30 seconds or so) of not having a backchannel connected, we'll evict the session completely. This will happen whenever a user closes their browser.
sessionTimeoutInterval: 30 * 1000
By default, browsers don't allow access via javascript to foreign sites. You can use the cors: option to set the Access-Control-Allow-Origin header in responses, which tells browsers whether or not to allow cross domain requests to be sent.
See https://developer.mozilla.org/en/http_access_control for more information.
Setting cors:'*' will enable javascript from any domain to access your application. BE CAREFUL! If your application uses cookies to manage user sessions, javascript on a foreign site could make requests as if it were acting on behalf of one of your users.
cors: null
A user can override all the headers if they want by setting the headers option to an object.
headers: null
All server responses set some standard HTTP headers. To be honest, I don't know how many of these are necessary. I just copied them from google.
The nocache headers in particular seem unnecessary since each client
request includes a randomized zx=junk
query parameter.
standardHeaders =
'Content-Type': 'text/plain'
'Cache-Control': 'no-cache, no-store, max-age=0, must-revalidate'
'Pragma': 'no-cache'
'Expires': 'Fri, 01 Jan 1990 00:00:00 GMT'
'X-Content-Type-Options': 'nosniff'
Gmail also sends this, though I'm not really sure what it does... 'X-Xss-Protection': '1; mode=block'
The one exception to that is requests destined for iframes. They need to have content-type: text/html set for IE to process the juicy JS inside.
ieHeaders = {}
ieHeaders[k] = v for k, v of standardHeaders
ieHeaders['Content-Type'] = 'text/html'
Google's browserchannel server adds some junk after the first message data is sent. I assume this stops some whole-page buffering in IE. I assume the data used is noise so it doesn't compress.
I don't really know why google does this. I'm assuming there's a good reason to it though.
ieJunk = "7cca69475363026330a0d99468e88d23ce95e222591126443015f5f462d9a177186c8701fb45a6ffe
e0daf1a178fc0f58cd309308fba7e6f011ac38c9cdd4580760f1d4560a84d5ca0355ecbbed2ab715a3350fe0c47
9050640bd0e77acec90c58c4d3dd0f5cf8d4510e68c8b12e087bd88cad349aafd2ab16b07b0b1b8276091217a44
a9fe92fedacffff48092ee693af\n"
If the user is using IE, instead of using XHR backchannel loaded using a forever iframe. When data is sent, it is wrapped in tags which call functions in the browserchannel library.
This method wraps the normal .writeHead()
, .write()
and .end()
methods by
special versions which produce output based on the request's type.
This is not used for:
messagingMethods = (options, query, res) ->
type = query.TYPE
if type == 'html'
junkSent = false
methods =
writeHead: ->
res.writeHead 200, 'OK', ieHeaders
res.write ''
domain = query.DOMAIN
If the iframe is making the request using a secondary domain, I think we need
to set the domain
to the original domain so that we can call the response methods.
if domain and domain != ''
Make sure the domain doesn't contain anything by naughty by JSON.stringify()
-ing
it before passing it to the client. There are XSS vulnerabilities otherwise.
res.write "\n"
write: (data) ->
The data is passed to m()
, which is bound to onTridentRpcMessage_ in the client.
res.write "\n"
unless junkSent
res.write ieJunk
junkSent = true
end: ->
Once the data has been received, the client needs to call d()
, which is bound to
onTridentDone_ with success=true.
The weird spacing of this is copied from browserchannel. Its really not necessary.
res.end "\n"
This is a helper method for signalling an error in the request back to the client.
writeError: (statusCode, message) ->
The HTML (iframe) handler has no way to discover that the embedded script tag didn't complete successfully. To signal errors, we return 200 OK and call an exposed rpcClose() method on the page.
methods.writeHead()
res.end "\n"
For some reason, sending data during the second test (111112) works slightly differently for XHR, but its identical for html encoding. We'll use a writeRaw() method in that case, which is copied in the case of html.
methods.writeRaw = methods.write
methods
else
For normal XHR requests, we send data normally.
writeHead: -> res.writeHead 200, 'OK', options.headers
write: (data) -> res.write "#{data.length}\n#{data}"
writeRaw: (data) -> res.write data
end: -> res.end()
writeError: (statusCode, message) ->
res.writeHead statusCode, options.headers
res.end message
For telling the client its done bad.
It turns out google's server isn't particularly fussy about signalling errors using the proper html RPC stuff, so this is useful for html connections too.
sendError = (res, statusCode, message) ->
res.writeHead statusCode, message
res.end "
#{message}
"
return
The client sends data in a series of url-encoded maps. The data is encoded like this:
count=2&ofs=0&req0_x=3&req0_y=10&req1_abc=def
First, we need to buffer up the request response and query string decode it.
bufferPostData = (req, callback) ->
data = []
req.on 'data', (chunk) ->
data.push chunk.toString 'utf8'
req.on 'end', ->
data = data.join ''
callback data
Next, we'll need to decode the incoming client data into an array of objects.
The data could be in two different forms:
We can tell what format the data is in by inspecting the content-type header
Essentially, url encoded the data looks like this:
{ count: '2',
ofs: '0',
req0_x: '3',
req0_y: '10',
req1_abc: 'def'
}
... and we will return an object in the form of [{x:'3', y:'10'}, {abc: 'def'}, ...]
JSON encoded the data looks like:
{ ofs: 0
, data: [null, {...}, 1000.4, 'hi', ...]
}
or null
if there's no data.
This function returns null if there's no data or {ofs, json:[...]} or {ofs, maps:[...]}
transformData = (req, data) ->
if req.headers['content-type'] == 'application/json'
We'll restructure it slightly to mark the data as JSON rather than maps.
{ofs, data} = data
{ofs, json:data}
else
count = parseInt data.count
return null if count is 0
ofs will be missing if count is zero
ofs = parseInt data.ofs
throw new Error 'invalid map data' if isNaN count or isNaN ofs
throw new Error 'Invalid maps' unless count == 0 or (count > 0 and data.ofs?)
maps = new Array count
Scan through all the keys in the data. Every key of the form:
req123_xxx
will be used to populate its map.
regex = /^req(\d+)_(.+)$/
for key, val of data
match = regex.exec key
if match
id = match[1]
mapKey = match[2]
map = (maps[id] ||= {})
The client uses mapX_type=_badmap
to signify an error encoding a map.
continue if id == 'type' and mapKey == '_badmap'
map[mapKey] = val
{ofs, maps}
Decode data string body and get an object back Either a query string format or JSON depending on content type
decodeData = (req, data) ->
if req.headers['content-type'] == 'application/json'
JSON.parse data
else
Maps. Ugh.
By default, querystring.parse only parses out the first 1000 keys from the data. maxKeys:0 removes this restriction.
querystring.parse data, '&', '=', maxKeys:0
This is a helper method to order the handling of messages / requests / whatever.
Use it like this: inOrder = order 0
inOrder 1, -> console.log 'second' inOrder 0, -> console.log 'first'
Start is the ID of the first element we expect to receive. If we get data for earlier elements, we'll play them anyway if playOld is truthy.
order = (start, playOld) ->
Base is the ID of the (missing) element at the start of the queue
base = start
The queue will start with about 10 elements. Elements of the queue are undefined if we don't have data for that queue element.
queue = new Array 10
(seq, callback) ->
Its important that all the cells of the array are truthy if we have data. We'll use an empty function instead of null.
callback or= ->
Ignore old messages, or play them back immediately if playOld=true
if seq < base
callback() if playOld
else
queue[seq - base] = callback
while queue[0]
callback = queue.shift()
base++
callback()
We need access to the client's sourcecode. I'm going to get it using a synchronous file call (it'll be fast anyway, and only happen once).
I'm also going to set an etag on the client data so the browser client will be cached. I'm kind of uncomfortable about adding complexity here because its not like this code hasn't been written before, but.. I think a lot of people will use this API.
I should probably look into hosting the client code as a javascript module using that client-side npm thing.
clientFile = "#{__dirname}/../dist/bcsocket.js"
clientStats = fs.statSync clientFile
try
clientCode = fs.readFileSync clientFile, 'utf8'
catch e
console.error 'Could not load the client javascript. Run `cake client` to generate it.'
throw e
This is mostly to help development, but if the client is recompiled, I'll pull in a new version. This isn't tested by the unit tests - but its not a big deal.
The readFileSync
call here will stop the whole server while the client is reloaded.
This will only happen during development so its not a big deal.
if process.platform is "win32"
Windows doesn't support watchFile. See: https://github.com/josephg/node-browserchannel/pull/6
fs.watch clientFile, persistent: false, (event, filename) ->
if event is "change"
console.log "Reloading client JS"
clientCode = fs.readFileSync clientFile, 'utf8'
clientStats = curr
else
fs.watchFile clientFile, persistent: false, (curr, prev) ->
if curr.mtime.getTime() isnt prev.mtime.getTime()
console.log "Reloading client JS"
clientCode = fs.readFileSync clientFile, 'utf8'
clientStats = curr
The server module returns a function, which you can call with your configuration options. It returns your configured connect middleware, which is actually another function.
module.exports = browserChannel = (options, onConnect) ->
if typeof onConnect == 'undefined'
onConnect = options
options = {}
options ||= {}
options[option] ?= value for option, value of defaultOptions
options.headers = {} unless options.headers
options.headers[h] ||= v for h, v of standardHeaders
options.headers['Access-Control-Allow-Origin'] = options.cors if options.cors
Strip off a trailing slash in base.
base = options.base
base = base[... base.length - 1] if base.match /\/$/
Add a leading slash back on base
base = "/#{base}" unless base.match /^\//
map from sessionId -> session
sessions = {}
Host prefixes provide a way to skirt around connection limits. They're only really important for old browsers.
getHostPrefix = ->
if options.hostPrefixes
randomArrayElement options.hostPrefixes
else
null
This method will start a new client session.
Session ids are generated by node-hat. They are guaranteed to be unique.
This method is synchronous, because a database will never be involved in browserchannel session management. Browserchannel sessions only last as long as the user's browser is open. If there's any connection turbulence, the client will reconnect and get a new session id.
Sometimes a client will specify an old session ID and old array ID. In this case, the client is reconnecting and we should evict the named session (if it exists).
createSession = (address, query, headers) ->
{RID:initialRid, CVER:appVersion, OSID:oldSessionId, OAID:oldArrayId} = query
if oldSessionId? and (oldSession = sessions[oldSessionId])
oldSession._acknowledgeArrays oldArrayId
oldSession.close 'Reconnected'
Create a new session. Sessions extend node's EventEmitter so they have access to
goodies like session.on(event, handler)
, session.emit('paarty')
, etc.
session = new EventEmitter
The session's unique ID for this connection
session.id = hat()
The client stores its IP address and headers from when it first opened the session. The handler can use this information for authentication or something.
session.address = address
session.headers = headers
The session is a little state machine. It has the following states:
init: The session has been created and its sessionId hasn't been sent yet. The session moves to the ok state when the first data chunk is sent to the client.
ok: The session is sitting pretty and ready to send and receive data. The session will spend most of its time in this state.
closed: The session has been removed from the session list. It can no longer be used for any reason.
It is invalid to send arrays to a session while it is closed. Unless you're Bruce Willis...
session.state = 'init'
The state is modified through this method. It emits events when the state changes. (yay)
changeState = (newState) ->
oldState = session.state
session.state = newState
session.emit 'state changed', session.state, oldState
The server sends messages to the client via a hanging GET request. Of course, the client has to be the one to open that request.
This is a handle to null, or {res, methods, chunk}
backChannel = null
The server sends data to the client by sending arrays. It seems a bit silly that client->server messages are maps and server->client messages are arrays, but there it is.
Each entry in this array is of the form [id, data].
outgoingArrays = []
lastArrayId
is the array ID of the last queued array
lastArrayId = -1
Every request from the client has an AID parameter which tells the server the ID of the last request the client has received. We won't remove arrays from the outgoingArrays list until the client has confirmed its received them.
In lastSentArrayId
we store the ID of the last array which we actually sent.
lastSentArrayId = -1
I would like this method to be private or something, but it needs to be accessed from the HTTP request code below. The _ at the start will hopefully make people think twice before using it.
session._setBackChannel = (res, query) ->
clearBackChannel()
backChannel =
res: res
methods: messagingMethods options, query, res
chunk: query.CI == '0'
bytesSent: 0
listener: ->
backChannel.listener = null
clearBackChannel res
When the TCP connection underlying the backchannel request is closed, we'll stop using the backchannel and start the session timeout clock. The listener is kept so the event handler removed once the backchannel is closed.
res.connection.once 'close', backChannel.listener
We'll start the heartbeat interval and clear out the session timeout. The session timeout will be started again if the backchannel connection closes for any reason.
refreshHeartbeat()
clearTimeout sessionTimeout
When a new backchannel is created, its possible that the old backchannel is dead. In this case, its possible that previously sent arrays haven't been received. By resetting lastSentArrayId, we're effectively rolling back the status of sent arrays to only those arrays which have been acknowledged.
lastSentArrayId = outgoingArrays[0].id - 1 if outgoingArrays.length > 0
Send any arrays we've buffered now that we have a backchannel
@flush()
If we haven't sent anything for 15 seconds, we'll send a little ['noop']
to the
client so it knows we haven't forgotten it. (And to make sure the backchannel
connection doesn't time out.)
heartbeat = null
This method removes the back channel and any state associated with it. It'll get called when the backchannel closes naturally, is replaced or when the connection closes.
clearBackChannel = (res) ->
clearBackChannel doesn't do anything if we call it repeatedly.
return unless backChannel
Its important that we only delete the backchannel if the closed connection is actually the backchannel we're currently using.
return if res? and res != backChannel.res
if backChannel.listener
The backchannel listener has been attached to the 'close' event of the underlying TCP stream. We don't care about that anymore
backChannel.res.connection.removeListener 'close', backChannel.listener
backChannel.listener = null
Conveniently, clearTimeout has no effect if the argument is null.
clearTimeout heartbeat
backChannel.methods.end()
backChannel = null
Whenever we don't have a backchannel, we run the session timeout timer.
refreshSessionTimeout()
This method sets / resets the heartbeat timeout to the full 15 seconds.
refreshHeartbeat = ->
clearTimeout heartbeat
heartbeat = setInterval (-> session.send ['noop']), options.keepAliveInterval
The session will close if there's been no backchannel for awhile.
sessionTimeout = null
refreshSessionTimeout = ->
clearTimeout sessionTimeout
sessionTimeout = setTimeout (-> session.close 'Timed out'), options.sessionTimeoutInterval
Since the session doesn't start with a backchannel, we'll kick off the timeout timer as soon as its created.
refreshSessionTimeout()
The arrays get removed once they've been acknowledged
session._acknowledgeArrays = (id) ->
id = parseInt id if typeof id is 'string'
while outgoingArrays.length > 0 and outgoingArrays[0].id <= id
{confirmcallback} = outgoingArrays.shift()
I've got no idea what to do if we get an exception thrown here. The session will end up in an inconsistant state...
confirmcallback?()
return
Queue an array to be sent. The optional callbacks notifies a caller when the array has been sent, and then received by the client.
If the session is already closed, we'll call the confirmation callback immediately with the error.
queueArray returns the ID of the queued data chunk.
queueArray = (data, sendcallback, confirmcallback) ->
return confirmcallback? new Error 'closed' if session.state is 'closed'
id = ++lastArrayId
outgoingArrays.push {id, data, sendcallback, confirmcallback}
lastArrayId
The session has just been created. The first thing it needs to tell the client is its session id and host prefix and stuff.
It would be pretty easy to add a callback here setting the client status to 'ok' or something, but its not really necessary. The client has already connected once the first POST /bind has been received.
queueArray ['c', session.id, getHostPrefix(), 8]
Send the array data through the backchannel. This takes an optional callback which will be called with no arguments when the client acknowledges the array, or called with an error object if the client disconnects before the array is sent.
queueArray can also take a callback argument which is called when the session sends the message in the first place. I'm not sure if I should expose this through send - I can't tell if its useful beyond the server code.
session.send = (arr, callback) ->
id = queueArray arr, null, callback
@flush()
id
The client sends maps to the server using POST requests. Its possible for the requests to come in out of order, so sometimes we need to buffer up incoming maps and reorder them before emitting them to the user.
Each map has an ID (which starts at 0 when the session is first created).
We'll emit received data to the user immediately if they're in order, but if they're out of order we'll use the little order helper above to order them. The order helper is instructed to not emit any old messages twice.
There's a potential DOS attack here whereby a client could just spam the server with out-of-order maps until it runs out of memory. We should dump a session if there are too many entries in this dictionary.
mapBuffer = order 0, false
This method is called whenever we get maps from the client. Offset is the ID of the first map. The data could either be maps or JSON data. If its maps, data contains {maps} and if its JSON data, maps contains {JSON}.
Browserchannel has 2 different mechanisms for consistantly ordering messages in the forward channel:
Each forward channel request contains a request ID (RID=X), which start at a random value (set with the first session create packet). These increment by 1 with each request.
If a request fails, it might be retried with the same RID as the previous message, and with extra maps tacked on the end. We need to handle the maps in this case.
Each map has an ID, counting from 0. ofs= in the POST data tells the server the ID of the first map in a request.
As far as I can tell, the RID stuff can mostly be ignored. The one place it is important is in handling disconnect messages. The session should only be disconnected by a disconnect message when the preceeding messages have been received.
All requests are handled in order too, though if not for disconnecting I don't think it would matter. Because of the funky retry-has-extra-maps logic, we'll allow processing requests twice.
ridBuffer = order initialRid, true
session._receivedData = (rid, data) ->
ridBuffer rid, ->
return if data is null
throw new Error 'Invalid data' unless data.maps? or data.json?
ridBuffer rid
id = data.ofs
First, classic browserchannel maps.
if data.maps
If an exception is thrown during this loop, I'm not really sure what the behaviour should be.
for map in data.maps
The funky do expression here is used to pass the map into the closure. Another way to do it is to index into the data.maps array inside the function, but then I'd need to pass the index to the closure anyway.
mapBuffer id++, do (map) -> ->
return if session.state is 'closed'
session.emit 'map', map
If you specify the key as JSON, the server will try to decode JSON data from the map and emit 'message'. This is a much nicer way to message the server.
if map.JSON?
try
message = JSON.parse map.JSON
catch e
session.close 'Invalid JSON'
return
session.emit 'message', message
else
We have data.json. We'll just emit it directly.
for message in data.json
mapBuffer id++, do (map) -> ->
return if session.state is 'closed'
session.emit 'message', message
session._disconnectAt = (rid) ->
ridBuffer rid, -> session.close 'Disconnected'
When we receive forwardchannel data, we reply with a special little 3-variable array to tell the client if it should reopen the backchannel.
This method returns what the forward channel should reply with.
session._backChannelStatus = ->
Find the arrays have been sent over the wire but haven't been acknowledged yet
numUnsentArrays = lastArrayId - lastSentArrayId
unacknowledgedArrays = outgoingArrays[... outgoingArrays.length - numUnsentArrays]
outstandingBytes = if unacknowledgedArrays.length == 0
0
else
We don't care about the length of the array IDs or callback functions. I'm actually not sure what data the client expects here - the value is just used in a rough heuristic to determine if the backchannel should be reopened.
data = (a.data for a in unacknowledgedArrays)
JSON.stringify(data).length
[
(if backChannel then 1 else 0)
lastSentArrayId
outstandingBytes
]
The server sends data to the client in chunks. Each chunk is a JSON array prefixed by its length in bytes.
The array looks like this:
[
[100, ['message', 'one']],
[101, ['message', 'two']],
[102, ['message', 'three']]
]
Each individial message is prefixed by its array id, which is a counter starting at 0 when the session is first created and incremented with each array.
This will actually send the arrays to the backchannel on the next tick if the backchannel is alive.
session.flush = ->
process.nextTick ->
if backChannel
numUnsentArrays = lastArrayId - lastSentArrayId
if numUnsentArrays > 0
arrays = outgoingArrays[outgoingArrays.length - numUnsentArrays ...]
I've abused outgoingArrays to also contain some callbacks. We only send [id, data] to the client.
data = ([id, data] for {id, data} in arrays)
bytes = JSON.stringify(data) + "\n"
Stand back, pro hax! Ideally there is a general solution for escaping these characters when converting to JSON.
bytes = bytes.replace(/\u2028/g, "\\u2028")
bytes = bytes.replace(/\u2029/g, "\\u2029")
Away!
backChannel.methods.write bytes
backChannel.bytesSent += bytes.length
lastSentArrayId = lastArrayId
Fire any send callbacks on the messages. These callbacks should only be called once. Again, not sure what to do if there are exceptions here.
for a in arrays
if a.sendcallback?
a.sendcallback?()
delete a.sendcallback
The send callback could have cleared the backchannel by calling close.
if backChannel and (!backChannel.chunk or backChannel.bytesSent > 10 * 1024)
clearBackChannel()
The first backchannel is the client's initial connection. Once we've sent the first data chunk to the client, we've officially opened the connection.
changeState 'ok' if session.state == 'init'
The client's reported application version, or null. This is sent when the connection is first requested, so you can use it to make your application die / stay compatible with people who don't close their browsers.
session.appVersion = appVersion or null
Signal to a client that it should stop trying to connect. This has no other effect on the server session.
stop
takes a callback which will be called once the message has been sent by the server.
Typically, you should call it like this:
session.stop ->
session.close()
I considered making this automatically close the connection after you've called it, or after you've sent the stop message or something, but if I did that it wouldn't be obvious that you can still receive messages after stop() has been called. (Because you can!). That would never come up when you're testing locally, but it would come up in production. This is more obvious.
session.stop = (callback) ->
return if @state is 'closed'
queueArray ['stop'], callback, null
@flush()
This closes a session and makes the server forget about it.
The client might try and reconnect if you only call close()
. It'll get a new session if it does so.
close takes an optional message argument, which is passed to the send event handlers.
session.close = (message) ->
You can't double-close.
return if @state == 'closed'
changeState 'closed'
@emit 'close', message
clearBackChannel()
clearTimeout sessionTimeout
for {confirmcallback} in outgoingArrays
confirmcallback? new Error(message || 'closed')
delete sessions[@id]
console.log "closed #{@id}"
sessions[session.id] = session
session
This is the returned middleware. Connect middleware is a function which takes in an http request, an http response and a next method.
The middleware can do one of two things:
next()
, which allows the next middleware in the stack a chance to
handle the request. middleware = (req, res, next) ->
{query, pathname} = parse req.url, true
console.warn req.method, req.url
If base is /foo, we don't match /foobar. (Currently no unit tests for this)
return next() if pathname.substring(0, base.length + 1) != "#{base}/"
{writeHead, write, writeRaw, end, writeError} = messagingMethods options, query, res
The browserchannel server hosts a usable web client library at /CHANNEL/bcsocket.js. This library wraps the google closure library client implementation.
If I have time, I would like to write my own version of the client to add a few features (websockets, message acknowledgement callbacks) and do some manual optimisations for speed. However, the current version works ok.
if pathname is "#{base}/bcsocket.js"
etag = "\"#{clientStats.size}-#{clientStats.mtime.getTime()}\""
res.writeHead 200, 'OK',
'Content-Type': 'application/javascript',
'ETag': etag,
'Content-Length': clientCode.length
This code is manually tested because it looks like its impossible to send HEAD requests using nodejs's HTTP library at time of writing (0.4.12). (Yeah, I know, rite?)
if req.method is 'HEAD'
res.end()
else
res.end clientCode
Before the browserchannel client connects, it tests the connection to make sure its working, and to look for buffering proxies.
The server-side code for connection testing is completely stateless.
else if pathname is "#{base}/test"
This server only supports browserchannel protocol version 8. I have no idea if 400 is the right error here.
return sendError res, 400, 'Version 8 required' unless query.VER is '8'
The client is requests host prefixes. The server responds with an array of ['hostprefix' or null, 'blockedprefix' or null].
Actually, I think you might be able to return [] if neither hostPrefix nor blockedPrefix is defined. (Thats what google wave seems to do)
hostprefix is subdomain prepended onto the hostname of each request. This gets around browser connection limits. Using this requires a bank of configured DNS entries and SSL certificates if you're using HTTPS.
blockedprefix provides network admins a way to blacklist browserchannel requests. It is not supported by node-browserchannel.
if query.MODE == 'init' and req.method == 'GET'
hostPrefix = getHostPrefix()
blockedPrefix = null # Blocked prefixes aren't supported.
We add an extra special header to tell the client that this server likes json-encoded forward channel data over form urlencoded channel data.
It might be easier to put these headers in the response body or increment the version, but that might conflict with future browserchannel versions.
headers = {}
headers[k] = v for k, v of options.headers
headers['X-Accept'] = 'application/json; application/x-www-form-urlencoded'
This is a straight-up normal HTTP request like the forward channel requests. We don't use the funny iframe write methods.
res.writeHead 200, 'OK', headers
res.end(JSON.stringify [hostPrefix, blockedPrefix])
else
The client is trying to determine if their connection is buffered or unbuffered. We reply with '11111', then 2 seconds later '2'.
The client should get the data in 2 chunks - but they won't if there's a misbehaving corporate proxy in the way or something.
writeHead()
writeRaw '11111'
setTimeout (-> writeRaw '2'; end()), 2000
Once a client has finished testing its connection, it connects.
BrowserChannel communicates through two connections:
else if pathname == "#{base}/bind"
I'm copying the behaviour of unknown SIDs below. I don't know how the client
is supposed to detect this error, but, eh. The other choice is to return writeError ...
return sendError res, 400, 'Version 8 required' unless query.VER is '8'
All browserchannel connections have an associated client object. A client is created immediately if the connection is new.
if query.SID
session = sessions[query.SID]
This is a special error code for the client. It tells the client to abandon its connection request and reconnect.
For some reason, google replies with the same response on HTTP and HTML requests here. I'll follow suit, though its a little weird. Maybe I should do the same with all client errors?
return sendError res, 400, 'Unknown SID' unless session
session._acknowledgeArrays query.AID if query.AID? and session
if req.method == 'POST'
if session == undefined
The session is new! Make them a new session object and let the application know.
session = createSession req.connection.remoteAddress, query, req.headers
onConnect? session
dataError = (e) ->
console.warn 'Error parsing forward channel', e.stack
return sendError res, 400, 'Bad data'
processData = (data) ->
try
data = transformData req, data
session._receivedData query.RID, data
catch e
return dataError e
if session.state is 'init'
The initial forward channel request is also used as a backchannel for the server's initial data (session id, etc). This connection is a little bit special - it is always encoded using length-prefixed json encoding and it is closed as soon as the first chunk is sent.
res.writeHead 200, 'OK', options.headers
session._setBackChannel res, CI:1, TYPE:'xmlhttp', RID:'rpc'
session.flush()
else if session.state is 'closed'
If the onConnect handler called close() immediately, session.state can be already closed at this point. I'll assume there was an authentication problem and treat this as a forbidden connection attempt.
sendError res, 403, 'Forbidden'
else
On normal forward channels, we reply to the request by telling the session if our backchannel is still live and telling it how many unconfirmed arrays we have.
response = JSON.stringify session._backChannelStatus()
res.writeHead 200, 'OK', options.headers
res.end "#{response.length}\n#{response}"
if req.body
processData req.body
else
bufferPostData req, (data) ->
try
data = decodeData req, data
catch e
return dataError e
processData data
else if req.method is 'GET'
GET messages are usually backchannel requests (server->client). Backchannel messages are handled by the session object.
if query.TYPE in ['xmlhttp', 'html']
return sendError res, 400, 'Invalid SID' if typeof query.SID != 'string' && query.SID.length < 5
return sendError res, 400, 'Expected RPC' unless query.RID is 'rpc'
writeHead()
session._setBackChannel res, query
The client can manually disconnect by making a GET request with TYPE='terminate'
else if query.TYPE is 'terminate'
We don't send any data in the response to the disconnect message.
The client implements this using an img= appended to the page.
session?._disconnectAt query.RID
res.writeHead 200, 'OK', options.headers
res.end()
else
res.writeHead 405, 'Method Not Allowed', options.headers
res.end "Method not allowed"
else
We'll 404 the user instead of letting another handler take care of it. Users shouldn't be using the specified URL prefix for anything else.
res.writeHead 404, 'Not Found', options.headers
res.end "Not found"
middleware.close = -> session.close() for id, session of sessions
This is an undocumented, untested treat - if you pass the HTTP server / connect server to browserchannel through the options object, it can attach a close listener for you automatically.
options.server?.on 'close', middleware.close
middleware
This will override the timer methods (setInterval
, etc) with the testing stub versions,
which are way faster.
browserChannel._setTimerMethods = (methods) ->
{setInterval, clearInterval, setTimeout, clearTimeout, Date} = methods


I once played bass in a punk band, but now drive business decisions using data.
I hold an MBA from the UCLA Anderson School of Management. My MBA focus was on strategy and entrepreneurship. My master's thesis involved creating a partnership with a Los Angeles solar startup with a team of 4. I analyzed a combination of public and private datasets (>3M observations) to target a new customer segment based on quantitative and qualitative criteria that identified customer fit for solar rooftop systems. At Anderson I was a Consortium Fellow (full-tuition), two-time Management Development for Entrepreneurs Fellow, Tiger 21 ELDP Team Leader, and president of the Black and Scholes Surf Club.
University of California, Los Angeles - Anderson School of Management
California State University, Long Beach
Invoice2go Cross-platform invoicing and payments SaaS product for SMBs ($50M in venture funding)
Analysis Group Leading economic, financial, and strategy consulting firm ($315M revenue)
Data: Python, R, SAS, SQL
Web: Javascript, HTML, CSS
Bicycle commuting, DIY projects, nonfiction (The Food Lab, Freakonomics series, Sapiens) and science fiction (Foundation series, The Martian, Ready Player One)


↵
Leeds Ward Data:
Display ward data from a dataset on the
Leeds Data Mill
as a hexagon tile map.
Leeds Data Mill Database ID
Ward column title
Category column title
Colour
Category to display
Otley and Yeadon
Adel and Wharfedale
Alwoodley
Guiseley and Rawdon
Horsforth
Weetwood
Moortown
Harewood
Wetherby
Pudsey
Calverley and Farsley
Kirkstall
Headingley
Chapel Allerton
Roundhay
Cross Gates and Whinmoor
Bramley and Stanningley
Armley
Hyde Park and Woodhouse
Gipton and Harehills
Killingbeck and Seacroft
Farnley and Wortley
Beeston and Holbeck
City and Hunslet
Burmantofts and Richmond Hill
Temple Newsam
Kippax and Methley
Morley North
Morley South
Middleton Park
Rothwell
Garforth and Swillington
Ardsley and Robin Hood
Leeds


A generic way of handling loading-status, saving-status and validation errors in Angular
When writing Angular applications there are always pieces of functionality that are being rewritten over and over again. 3 common usecases are:
- Showing a loading status
- Showing an acting status (whether the user is adding, updating or removing data)
- Showing validation errors
In this article we are going to implement a generic solution on how to fix these usecases.
A non generic way of loading, saving and handling validation errors in Angular
Before we jump to the solution, let’s have a look at the impact of a non generic way of handling the previous called functionalities. A solution that is often used might look like the following: (keep in mind that this functionality has to be implemented over and over again for every component)
ngOnInit(): void {
this.loading = true;
const usersCompleted = false;
const citiesCompleted = false;
this.userService.fetch().subscribe(users => {
usersCompleted = true;
this.loading = usersCompleted && citiesCompleted;
});
this.citiesService.fetch().subscribe(cities => {
citiesCompleted = true;
this.loading = usersCompleted && citiesCompleted;
});
}
We have to keep track of which call completes first because we can’t set loading to false, if there is call still busy. What happens when there is an error? We would have to implement that as well.
Now this code is only for fetching two lists of data, this becomes ugly pretty quickly and the worst thing about this is that we have to reimplement that for every component that does data fetching.
It becomes even worse if we want to update, add and remove data. Imagine that we have to handle validation errors as well:
remove(user: User): void {
// TODO: set acting to true
this.userService.remove(user).subscribe(res => {
// TODO: set acting to false
});
}
update(user: User): void {
// TODO: set acting to true
this.userService.update(user).subscribe(res => {
// TODO: set acting to false
// TODO: handle validation errors
});
}
add(user: User): void {
// TODO: set acting to true
this.userService.add(user).subscribe(res => {
// TODO: set acting to false
// TODO: handle validation errors
});
}
To handle validation errors we have to check if the HTTP status code is 400, manually map the data etc.
These code samples are in this article to prove a point. It’s dirty redundant logic that we have to implement over and over again
Let’s clean this up
To achieve this we will use an Angular service in combination with an angular interceptor and Typescript decorators.
The first thing we need is a HttpStatusService
that exposes 3 observables:
loading$
: whether the user is fetching dataacting$
: whether the user is removing, updating or adding datavalidationErrors$
: whether there are validation errors or not
// http-status.service.ts
@Injectable({
// important to provide this service to the
// injector of the root module
providedIn: 'root'
})
export class HttpStatusService {
// regular subject because we don't want to replay
// the validationerrors
private validationErrorsSub$
= new Subject<ValidationError[]>();
// 2 subjects that replays the last value
// (ideal for state)
private loadingSub$ = new ReplaySubject<boolean>(1);
private actingSub$ = new ReplaySubject<boolean>(1);
// we don't want to expose the subject for
// encapsulation purposes. That's why we convert them
// into observables
getvalidationErrors$ =
this.validationErrorsSub$.asObservable();
loading$ =
this.loadingSub$.pipe(distinctUntilChanged());
acting$ =
this.actingSub$.pipe(distinctUntilChanged());
// these are just some regular setters to next
// the values in our subjects
set validationErrors(errors: ValidationError[]) {
this.validationErrorsSub$.next(errors);
}
set loading(val: boolean) {
this.loadingSub$.next(val);
}
set acting(val: boolean) {
this.actingSub$.next(val);
}
}
So we have a service that basically holds the state of our three statuses. Now we still have to make sure that the setters of these observables are being called at the right place and the right time. We don’t want to manually implement that for every call, so let’s create an interceptor for that.
@Injectable({
providedIn: 'root'
})
export class HttpStatusInterceptor implements HttpInterceptor {
// keep track of the loading calls
private loadingCalls = 0;
// keep track of the acting calls
private actingCalls = 0;
constructor(
private httpStatusService: HttpStatusService
) {}
private changeStatus(val: boolean, method: string): void {
if (['POST', 'PUT', 'DELETE', 'PATCH']
.indexOf(method) > -1) {
val ? this.actingCalls++ : this.actingCalls--;
this.httpStatusService.acting = this.actingCalls > 0;
} else if (method === 'GET') {
val ? this.loadingCalls++ : this.loadingCalls--;
this.httpStatusService.loading = this.loadingCalls > 0;
}
}
...
}
As we can see, we have created a private changeStatus()
function that will use the loading
and acting
setters of our HttpStatusInterceptor
class.
If the HTTP-method is POST
, PUT
, DELETE
or PATCH
we have to update the counter of the actingCalls
and if that count is bigger then 0, it means the user is acting and we have to update the acting
property of the HttpStatusService
.
If the HTTP-method is GET
it should do the same for the loadingCalls
property and loading
setter of the HttpStatusService
.
Of course we still have to implement the intercept
function. Every time we intercept a call we have to change a status to true
.
Because this means that the user is loading or acting.
The goal of an interceptor is to intercept a request, clone that request, do something with it and return it.
The handle
function of the HttpHandler
returns an observable. This is the perfect place to apply a finalize
operator,
which we can use to set a status to false
.
...
intercept(
req: HttpRequest<any>,
next: HttpHandler
): Observable<HttpEvent<any>> {
// there is a new request, so we are definitely
// loading or acting, we have to change the status
this.changeStatus(true, req.method);
return next.handle(req.clone()).pipe(
// when the request completes, errors or times out,
// we have to change the status as well
finalize(() => {
this.changeStatus(false, req.method);
})
);
}
This should automatically update the loading$
and acting$
observables in the HttpStatusService
.
However, we have not fixed our validation errors issue yet. For that we can use the catchError
operator that we place before the finalize operator:
intercept(
req: HttpRequest<any>,
next: HttpHandler
): Observable<HttpEvent<any>> {
..
return next.handle(req.clone()).pipe(
// catch the error
catchError(e => {
// if bad request > validation erors
if (e.status === 400) {
// use the validationErrors setter to update
this.httpStatusService.validationErrors =
e.error.validationErrors;
// make sure that this result never
// reaches the component
return NEVER;
}
// throw the error back
// or put that in the `HttpStatusService` as well ;-)
return throwError(e);
}),
finalize(...)
);
}
The entire interceptor class looks like this:
@Injectable({
providedIn: 'root'
})
export class HttpStatusInterceptor implements HttpInterceptor {
private loadingCalls = 0;
private actingCalls = 0;
constructor(
private httpStatusService: HttpStatusService
) {}
private changeStatus(v: boolean, method: string): void {
if (['POST', 'PUT', 'DELETE', 'PATCH']
.indexOf(method) > -1) {
v ? this.actingCalls++ : this.actingCalls--;
this.httpStatusService.acting = this.actingCalls > 0;
} else if (method === 'GET') {
v ? this.loadingCalls++ : this.loadingCalls--;
this.httpStatusService.loading = this.loadingCalls > 0;
}
}
intercept(
req: HttpRequest<any>,
next: HttpHandler
): Observable<HttpEvent<any>> {
return next.handle(req.clone()).pipe(
catchError(e => {
if (e.status === 400) {
this.httpStatusService.validationErrors =
e.error.validationErrors;
return NEVER;
}
return throwError(e);
}),
finalize(() => {
this.changeStatus(false, req.method);
})
);
}
}
When registering the interceptor to the root module, the observables in the HttpStatusService
will be updated automatically.
So in the rootModule we have to add the following code to the providers
property of the @NgModule
decorator:
providers: [
...
{
provide: HTTP_INTERCEPTORS,
multi: true,
deps: [HttpStatusService],
useClass: HttpStatusInterceptor
}
]
We can now use the HttpStatusService
in our code as easy as this:
export class UserComponent {
loading$ = this.httpStatus.loading;
validationErrors$ = this.httpStatus.validationErrors;
acting$ = this.httpStatus.loading;
constructor(
private httpStatusService: HttpStatusService) {
}
}
We now have 3 observables that can easily be consumed in the template of the component with the use of the async pipe. Here is an example:
<my-spinner *ngIf="loading$ | async"></my-spinner>
<my-user-form
[validationErrors]="validationErrors$ | async"
[disabled]="acting$ | async"></my-user-form>
Optimizing with decorators
We have cleaned up a lot, we found an easy way to get the httpstatuses to the component, but we can make it even simpler with the use of decorators. let’s refactor the UserComponent
class accordingly:
export class UserComponent {
@Loading()loading$;
@ValidationErrors() validationErrors$;
@Acting() acting$;
}
This is very declarative way of working. The component is way cleaner and we don’t have to inject the HttpStatusService
anymore.
But how do we create these decorators? I’m glad you asked, it’s pretty easy. A property decorator is simply a function that returns a function that gets the target and key as arguments.
export function Loading() {
return function (target: any, key: string): void {
// in this case the target is the component
// instance and key the property name
// target: userComponent, key: loading$
// now we have to set the property to the actual
// loading$ observable that lives in the HttpStatusService
target[key] = // todo
}
};
To set the property of the target we need to inject the HttpStatusService
instance that is registered on the root injector of our application. After all that’s the instance that contains the actual state. Currently there is no easy way to do that.
Until Angular provides us with functionality like that we can use the following solution:
Next to the http-status.service
file, create a file called root-injector.ts
and add the following code:
import { Injector } from '@angular/core';
export let rootInjector: Injector;
export function setRootInjector(injector: Injector): void {
rootInjector = injector;
}
The setRootInjector()
function will be used by the rootModule to set the rootInjector
variable that we expose here.
To make it work the rootModule will have to call the setRootInjector()
function like this:
@NgModule({
...
})
export class AppModule {
constructor(private injector: Injector) {
setRootInjector(injector);
}
}
The last step is to actually use the rootInjector
variable inside the decorators. The result looks like this:
// loading.decorator.ts
export function Loading() {
return function (target: any, key: string): void {
const service = rootInjector.get(HttpStatusService);
target[key] = service.loading$;
}
};
// acting.decorator.ts
export function Acting() {
return function (target: any, key: string): void {
const service = rootInjector.get(HttpStatusService);
target[key] = service.acting$;
}
};
// validation-errors.decorator.ts
export function ValidationErrors() {
return function (target: any, key: string): void {
const service = rootInjector.get(HttpStatusService);
target[key] = service.validationErrors$;
}
};
Conclusion
We have learned that we can remove the redundancy that comes with loading, acting and error handling statuses almost completely by the use of an interceptor, a simple service and a few decorators.
I hope you liked it!
Special thanks
A very special thanks to the reviewers:


To create clearer, more concise code provides this toolbox helps coders to isolate the essential parts of a script that produces a chosen result, such as an object, tables and figures written to disk and even warnings and errors. This work was funded by US National Science Foundation grant SSI-1450277 for applications of End-to-End Data Provenance.
| Version: | 1.0.0 |
| Imports: | igraph, jsonlite, formatR |
| Suggests: | roxygen2, testthat |
| Published: | 2018-03-23 |
| Author: | Matthew K. Lau |
| Maintainer: | Matthew K. Lau <matthewklau at fas.harvard.edu> |
| BugReports: | https://github.com/ProvTools/Rclean/issues |
| License: | GPL-3 | file LICENSE |
| URL: | https://github.com/ProvTools/Rclean |
| NeedsCompilation: | no |
| Materials: | README |
| CRAN checks: | Rclean results |
| Reference manual: | Rclean.pdf |
| Package source: | Rclean_1.0.0.tar.gz |
| Windows binaries: | r-devel: Rclean_1.0.0.zip, r-release: Rclean_1.0.0.zip, r-oldrel: Rclean_1.0.0.zip |
| OS X binaries: | r-release: Rclean_1.0.0.tgz, r-oldrel: Rclean_1.0.0.tgz |
Please use the canonical form https://CRAN.R-project.org/package=Rclean to link to this page.


Written by: Robert Carlock
Transcribed by: Coffee Mug
Dedicated to the great work of Eric Aasen, Guineapig and many, many more.
[Scene: Ross and Rachel's apartment. Ross is playing with Emma on the couch after just changing her diaper.]
Ross: And that's why, no matter what mommy says, we really were on a break. (baby talk) Yes we were! Yes we were! (picks Emma up) Come here gorgeous. (puts her on his knees and talks to her) Oh!   You're just a... a little bitty baby, you know that? But you've got... (in a softer voice) You've got big beautiful eyes... Yes you do... and a... and a big round belly. (emphasises the B's) Big baby butt!  Oh my God, you've never done that before, have you? You never done that before... Daddy made you laugh, huh? Well, daddy and Sir Mix Alot... What? What? You... you wanna hear some more? Uhm...(raps) My anaconda don't want none / unless you got buns hon... (Emma laughs again and Ross looks worried) I'm a terrible father!
OPENING CREDITS
[Scene: Central Perk. Joey sits at a table and Chandler and Monica enter.]
Chandler: Hi!
Joey: Hey... hey listen... What do you guys know about investments?
Chandler: How come?
Joey: Well, I'm starting to make good money on the show and I'm thinking... I should probably do something with it.
Monica: What do you do with your money now?
Joey: Well, I just tape it to the back of my toilet tank. (realises that anyone could have overheard that) I didn't say that! It's in a bank guarded by robots!
Chandler: Do you have any ideas?
Joey: Uh, yeah... This guy at work got me excited about going in on an emu farm. That'd be kinda cool huh? Pitchin' in on the weekends, helping to plant the emus...
Monica: Joe... Emus are birds. You raise them for meat.
Joey: (laughs) Yeah! Right! (points at Monica) People eat birds... Bird meat...  (laughs again) Or... or maybe just a wing or... (realises...)
Monica: Joey, I think you should consider something a little less risky. I mean, I think in this market, real estate is your best investment.The Fed. just lowered the rates and the interest on your mortgage is totally deductible. (looks at Chandler) That's right, I know some stuff!
Joey: Real estate, huh? Hmmm...
Monica: (very excited) Oh, and you know who's selling a great apartment? Richard!
Chandler: (imitating Monica) Oh, and you know whose knowledge of her ex-boyfriend is shocking? Monica!
Monica: My dad told me. They play golf together.
Chandler: Oh, well... Maybe I'll join them some time. I just hope the club doesn't slip out of my hand and beat the moustache off his face.
[Scene: Monica and Chandler's apartment. Rachel and Monica are sitting at the dinner table and Phoebe enters, knocking on the door.]
Phoebe: Hi!
Rachel and Monica: Hey!
Phoebe: Listen! You have to help me pick a dress 'cause I'm meeting Mike's parents tonight! (Rachel gasps)
Monica: Wow, the boyfriend's parents! That's a big step.
Phoebe: (sarcastically) Really? That hadn't occurred to me.
Monica: They just gonna love you, just be yourself.
Phoebe: They live on the upper east side on Park Avenue!
Rachel: Oh yeah, she can't be herself.
Phoebe: Okay, so... allright... Which dress? (she holds up two 'Phoebe' dresses, Rachel and Monica look at them... taking their time, don't wanting to hurt Phoebe) You can say "neither".
Rachel and Monica: Oh God, neither!
Monica: I'm sorry honey, but we're gonna take you shopping. It's gonna be fine.
Rachel: Yeah, totally! You are in such good hands. And I'm so good with meeting parents. With the father, you know, you want to flirt a little bit, but not in a gross way. Just kind of like: "Oh mr. Pincer, I can see where Wallis gets his good looks..."
Monica: You went out with Wallis Pincer?
Rachel: Uh, he took the SAT's for me.
Monica: I knew you didn't get a 1400!
Rachel: Ssshyeah, well, duh! I mean...
Phoebe: So... now... What about with Mike's mom?
Rachel: Oh, with the mother, just... just constantly tell her how amazing her son is. Take it from me, moms love me. Ross's mom one time actually said I'm like the daughter that she never had.
Monica: (Monica looks at Rachel in disbelief) She said WHAT?
Phoebe: (speaking louder and articulating) That's she's like the daughter she never had. (Phoebe points at her ears) Listen! (Monica looks at Phoebe in a duh! way)
[Scene: Ross and Rachel's apartment. Rachel enters the apartment.]
Rachel: Hi.
Ross: Hi.
Rachel: I just finished getting Phoebe all dressed to meet Mike's parents. She's so nervous, it's so sweet!
Ross: Guess what? I made Emma laugh today.
Rachel: (in disbelief) You WHAT? And I missed it?
 It was... it was like uhm... (Ross tries to impersonate Emma's laugh, but it comes out very squeaky, very high pitched. He laughs about himself but then looks at Rachel, realises that it sounded weird and straightens his face.) Only... only not creepy.
Rachel: Well... well, what did you do to make her laugh? (excited)
Ross: I uhm... Well, I sang... (Rachel gasps) well actually I rapped... Baby Got Back... (Rachel's face changes from excited to angry)
Rachel: You WHAT?
Ross: But you know what, if you think about it, it actually promotes a healthy uhm... body image... because... even big butts or uhm... juicy doubles.
Rachel: (disgusted) owwwww...
Ross: Please don't take her away from me!
[Scene: Richard's apartment. There's a knock on the door.The listing agent opens the door for Chandler and Joey.]
Catherine: Oh hi, come on in. I'm Catherine, the listing agent.
Joey: Hi I'm Joey. This is Chandler.
Chandler: So how come Richard's selling the place? Went bankrupt? Medical malpractice? Choked on his own moustache?
Catherine: Actually, he is buying a much bigger place. It's got a great view of Central Pa.....
Chandler: mmm That's enough about you!
Joey: Is there anything we should know about the apartment?
Catherine: All the appliances are included. There is a lot of light, a new kitchen... I think you guys would be very happy here... (Joey and Chandler both realise what she's assuming and start laughing.)
Chandler: No, no, no, no, no, NO! No, no... we're not together. We're not a couple. We're definately not a couple.
Catherine: Oh... Okay, sorry!
Joey: Well, you seem pretty insulted by that. What? I'm not good enough for you?
Chandler: We're not gonna have this conversation again... Look at this place. Why am I so intimidated by this guy? Pretentious art, this huge macho couch. When we know all he does is sit around all day crying about losing Monica to a real man! (laughs) You don't think he's here, do you? (Joey looks around)
Joey: You know what it is? It's a nice place but I gotta see I don't know if I see myself living here.  (Joey sits down on the couch, mimes opening a can and puts his hand down his pants) Yeah, I could see it.
Chandler: (Chandler looks around the place and his eye gets caught by Richard's video collection) Look at these videos.  Magnum Force, Dirty Harry, Cool Hand Luke... Oh my God!
Joey: What?
Chandler: There's a tape here with Monica's name on it.
Joey: Ooh! A tape with a girls name on it. It's probably a sex tape... (realises) Wait a minute... This says Monica... (looks around) And this is Richard's apartment... (realises some more)
Chandler: Get there faster! (Joey gasps and finally understands...)
[Scene: Mike's parents building. Phoebe gets out of the classy elevator, looking all dressed up like an older woman, and very un-Phoebe. She walks to the door and rings the doorbell.]
Mike: Wow!
Phoebe: I'm wearing pantyhose!
Mike: Great! Come on in! (Mike kisses her on the cheek. A butler walks in and takes Phoebe's coat.)
Phoebe: Oh, thank you! Oh... Oh my God, you're RICH!
Mike: No, my parents are rich.
Phoebe: Yeah, so... They gotta die someday. (Mike's parents walk in) HELLO!
Mike: Mom, dad, this is Phoebe. Phoebe, these are my parents: Theodore and Bitsy.
Phoebe: (in a very posh accent) Theodore... Bitsy... What a delight!
Bitsy: It's so nice to finally meet you!
Phoebe: And you... Your home is lovely.
Bitsy: Well thank you, I'll give you a tour later. It's actually three floors.
Phoebe: Holy crap!

Phoebe: Oh, try and stop me!
Mike: Hey... Wh... What are you doing?
Phoebe: (no accent) I'm trying to get your parents to like me.
Mike: Yeah, I'm sure they will, but you don't have to do this... I'm wanting them to get to know Phoebe, not (accent) Phoebe...
Phoebe: (accent) Got it! It... It's hard to stop...
Mike: Well, come on...
Theodore: Phoebe, these are our friends, Tom and Sue Angle.
Bitsy: Phoebe, come sit. Tell us a little bit about yourself... So where are you from?
Phoebe: (no accent) Uhm... Okay, well, allright, uhm... Originally I'm from upstate, but uhm... then my mom killed herself and my stepdad went to prison, so... I just moved to the city where uhm... I actually lived in a burned out Buick LeSabre for a while... (frowns are received) which was okay, that was okay, until uhm...  I got over it and uhm... anyway, now I'm uhm... a freelance massage therapist, uhm... which, you know, isn't always steady money but at least I don't pay taxes, huh... (everyone in the room finds it a bit surreal, which Phoebe realises and starts to talk in the accent again) So... where does everyone summer?
COMMERCIAL BREAK
[Scene: Mike's parents house again.]
Phoebe: God! God! This is not going well.
Mike: No, no, no, you're doing fine, really... Why don't you go talk to my dad?
Phoebe: Okay, okay, okay, okay... Still sure about me being myself?
Mike: Absolutely! Or maybe just a little less pimp spit.
Phoebe: So Theodore... I uhm... I can see where Mike gets his good looks from...
Theodore: Oh... Well...
Phoebe: Yeah... And that physique! You must work out all the time...
Theodore: Oh no, not all the time... I do the best I can...
Phoebe: Yeah I bet! Look out! (Phoebe punches Theodore right in the stomach)
Theodore: OH! OWWWWW! (Theodore grabs his stomach in agony)
Phoebe: Oh my God, are you okay?
Theodore: I recently had surgery.
Phoebe: I'm so sorry!
Theodore: No, I'll be fine... I just should check the stitches...
Phoebe: I really, really am sorry.
Theodore: How could you know. Why wouldn't you punch me in the stomach? (Theodore walks out... Mike walks towards Phoebe)
Mike: Uhm... Did you just hit my dad?
Phoebe: Yes... I'm sorry, I've never met a boyfriends parents before...
Mike: But, I mean, you have met... humans before, right? Look, why don't you go talk to my mom?
Phoebe: Yeah okay... yeah, your mom... okay... She looks nice, I can talk to her.
Mike: Yeah, you do that, and I go check my dad for signs of internal bleeding. (Mike walks away and Bitsy walks in the same direction.)
Phoebe: Yeah... Oh Bitsy, hi. Uhm... listen I just wanted to thank you again for having me here tonight.
Bitsy: Well, not at all...
Phoebe: Also uhm... I just want you to know what a wonderful man your son is.
Bitsy: Thank you, I think so too.
Phoebe: Well, and you know, it really is a testament to how he was raised. Especially to you. Because he's very respectful of women.
Bitsy: Is he really?
Phoebe: Are you kidding. He is so considerate of my feelings and... you know I think... you'd also like to know that he is a very gentle lover.
Bitsy: E-e-excuse me?
Phoebe: (Mike now enters and stands behind Phoebe) Oh no, no, no, no, no, no. Don't get me wrong. No, he's not in like a sissy way. No, no, no... when he gets going, he can rattle a headboard like a sailor on leave...
Bitsy: That's... my boy. (Bitsy walks away)
Mike: Awesome!
[Scene: Monica and Chandler's apartment. Only Chandler is there with the videotape in his hands, standing in front of the TV set.]
Chandler: I'm not gonna watch it... I don't NEED to watch it... I mean, what good could possibly come from watching? (sighs) Well, we know I'm gonna watch it. (Chandler moves to put the tape in the VCR and Joey enters the apartment)
Joey: Hey dude, what's up?
Chandler: Don't judge me, I'm only human!
Joey: Did you take that tape?
Chandler: I had to! Okay, imagine you were married... and you found a tape of your wife in another guys' apartment... Wouldn't you need to know what was on it?
Joey: I don't know. Who'm I'm married to?
Chandler: Some girl...!?
Joey: She hot?
Chandler: Yeah...!?
Joey: How did she get me to settle down?
Chandler: Allright, I'm gonna watch it... I mean look, it's probably not even what I think it is... And even if it is... It can't possibly be as bad as what I'm picturing in my head... (laughs nervously) Can it?
Joey: Guess I don't know. My experience: if a girl says yes to being taped... She doesn't say no to much else, I tell ya...
Chandler: Then you're gonna have to watch it for me.
Joey: (backs off) What? Whoo... What?
Chandler: Just for a few seconds, so I can know what it is... Please?
Joey: All right, fine... But if I enjoy this, you have only yourself to blame... (Chandler turns his back to the TV. Joey puts the tape in the VCR, switches it on and watches what's on the tape... It's clearly a american football match, with the referee's whistle blowing, the crowds cheering...)
Chandler: Why am I hearing cheering?
Joey: Well it's okay, its like... its just a football game.
Chandler: Football? Just football?
Joey: Yeah, see... you were all worried for nothing.
Chandler: It's football... It's just football... This is great! This is the first time I've ever enjoyed football... It may be customary to get a beer... (Chandler walks to the fridge, his back turned to the TV and a moaning sound replaces the cheering of the crowd... Joey's eyes double in size...) What the... (Chandler turns around, but Joey already took a sprint for Chandler, jumps, and floors Chandler in the open space in front of the apartment door...) What are you doing?
Joey: You don't wanna see what I just saw! (at this moment Monica comes home, and sees Chandler flat on his back on the floor and Joey pinning him down)
Monica: What are you guys doing? (Monica hears the moaning coming from the TV and looks at it) Oh my God, is that Richard? (It only takes a split second for Joey to realise, he pulls Monica down by her jacket, and she falls, face down next to Chandler. Chandler gets up a bit, and Joey quickly covers Chandler's eyes with his hand.)
[Scene: Ross and Rachel's apartment. Emma is sitting in her chair on the apothecary table and Rachel is trying to make her laugh.]
Rachel: Okay... aahhh... Please laugh for mommy... Please? Please laugh for mommy... (Rachel makes a funny face, sticking her tongue out, making a farting noise and using her hands as antlers, wiggling her fingers... No response from Emma...) Not funny huh? Oh so, is it... only offensive novelty rap? Or maybe just, you know, rap in general? 'Cause mommy can rap... (Rachel tries to rap and makes weird movements with her arms in the process.) My name is mommy and I'm here to say / that all the babies are... Oh, I can't rap... Allright sweetheart... This is only because I love you so much, and I know that you're not gonna tell anybody... (Rachel's face is telling "Oh what am I doing? The things I have to go through... and she starts to rap) I like... big butts and I cannot lie... / You other brothers can't deny... / when a girl walks in with an itty bitty waist and a round thing in your face... (Emma starts to laugh) Yes! Yes! Yes! YES! Oohhhhh! Oh! (Rachel now really gets into it, and her insecure movements start getting better) I like big butts and I cannot lie... / You other brothers can't deny... Oh Emma you're laughing!   (Rachel picks up Emma and Ross now enters)
Ross: Hey!
Rachel: Oh you missed it. She was laughing. Oh it was amazing. It was amazing. It was the most beautiful, beautiful sound that...
Ross: Oh I know, isn't it? Ooh... what'd you do to get her to laugh?
Rachel: Oh!  I just sang a little doo... Itsy Bitsy Spider...
Ross: You sang Baby Got Back didn't you?
Rachel: Nothing else worked. That girl is all about the ass...
[Scene: Mike's parents house, the dining room. Mike, Phoebe, Mike's parents and the Angles are there.]
Phoebe: ...and then it goes back to the chorus... Smelly cat, Sme-lly ca-t / I-t's not your fau-lt. And that's the end of the song... I realise that you didn't ask to hear it, but uhm... no-one had spoken in seventeen minutes.
Mike: Phoebe writes lots of great songs. Wha... What was that one you sang the other night that everybody just loved?
Phoebe: Oh, Pervert Parade?
Mike: (sighs) No...
Phoebe: Oh, Ode To A Pubic Hair?
Mike: Stop! (The butler serves dinner)
Phoebe: Oh God! Is that veal?
Mike: Mom, I thought I told you... Phoebe's a vegetarian.
Bitsy: Oh!
Phoebe: Oh no, no, no, no... That's okay, that's okay... I mean, I'm... I am a vegetarian... except for veal... Yeah no, veal I love...
Mike: Phoebe you don't have to eat...
Phoebe: No, no, no, I actually it's any baby animals: kittens, fish babies...  Clearly not enjoying the meat...) Hmmm... yummy (everybody seems okay with it, except Mike. He's making a hmmmm.... face... Then Phoebe swallows it) Hmmm... (at first she likes it, but then, in an instant puts her hand in front of her mouth and runs from the table. You hear a door slamming.)
Mike: So...? What do you think? (looks at his parents, which look in disgust)
[Scene: Monica and Chandler's. Monica switches off the VCR. Joey and Chandler are behind the couch.]
Monica: So you stole that tape from Richard's apartment?
Chandler: Whoho ho... Listen to the judgement from the porn star!
Monica: That tape was never meant to be seen by... (pauses) Joey I would feel more comfortable if I was having this conversation in private.
Joey: (laughs) Monica, look... I don't think you and I have any secrets anymore... (Monica keeps looking at Joey) Not ready to joke about it yet, okay, I see you later. (Joey walks out)
Monica: Why in the world would you take this tape and and why would you watch it?
Chandler: Because that's who I am, okay? I'm sure a mature man like Richard could see a tape like that and it wouldn't bother him. Just'd be another saucy anecdote for him to share at his men's club over brandy and moustaches.
Monica: Is all this about you not being able to grow a moustache?
Chandler: This is about you and Richard. He's clearly not over you. He keeps a tape so he can... look at it whenever he wants.
Monica: Isn't that sad?  You shouldn't be jealous. You should feel bad for him.
Chandler: Oh, yeah, well, poor Richard. Y'... I can grow a moustache!
Monica: Chandler, this is not our problem. We've got each other. That's all that matters.

Monica: Cowboy boots? I've never worn cowboy boots in my whole life! (she turns on the video again)
Chandler: Oh, good, good. Play more, 'cause I wanna see how it ends.

Chandler: What...? That's not you! Life is good again! Ride 'em cowgirl!
 (Chandler's expression changes)
Chandler: Is that a problem?
Monica: I-It's just so insulting! Big spring for a new blank tape, Doctor!
[Scene: Mike's parents house. Dining room again. Both Mike and Phoebe are not at the table, but the others still are.]
Theodore: I can't imagine what he sees in her.
Bitsy: She actually makes me miss that pill-popping ex-wife of his. (Mike walks in) Oh, hello dear...
Mike: Hey, what's going on?
Bitsy: We were just chit-chatting. How's your friend?
Mike: A little better.
Bitsy: By the way, do you know who's moving back into town? Tom and Sue's daughter Jen.
Theodore: You remember her Michael, she's lovely and... well behaved and... single.
Mike: I'm not interested.
Bitsy: Oh, please darling, let's be honest. You can have all the... sailor fun you want with that one, but... let's be real...
Mike: All right, stop! You know, all Phoebe has done tonight is trying to get you to like her. And maybe that hasn't been clear all the time, but she did her best. And yeah... She's a little different than you are...
 (Phoebe almost enters the room, but she hears the discussion and waits and evesdrops next to the door-opening.)
Mike: So what? I mean if even I can get past that, it shouldn't bother you. And you don't have to like her. You just have to accept the fact that I do. I mean, if you even can't be civil to the woman I love...
Bitsy: The woman you what? (Phoebe overheard what Michael said and now enters the room)
Phoebe: Yeah... The woman you what?
Mike: The woman I love... (he walks to Phoebe) I love you...
Phoebe: I love you too...
Mike: You do?
Phoebe: YEAH...! How great is this...? (they kiss)
Mike: Wanna get out of here?
Phoebe: Okay.
Mike: Mom, dad, thanks for dinner.
Phoebe: I had a great time. (accent) It was really top drawer. And here's something rich: thirteen bathrooms in this place... I threw up in the coat closet... Ta taaa...
COMMERCIAL BREAK
[Scene: Ross and Rachel's. Emma is in her bed and Ross and Rachel are rapping and dancing for her.]
Ross: She sweat, wet. got it going like a turbo 'vette.
Rachel: So fellas
Ross: Yeah!
Rachel: fellas
Ross: Yeah!
Rachel: has your girlfriend got the butt?
Ross: Hell yeah!
Rachel: So shake it! (Rachel slaps Ross's butt on the beat)
Ross: Shake it!
Rachel: Shake it! (Phoebe, Mike, Chandler, Monica and Joey step in)
Ross: Shake it!
Rachel: Shake that nasty butt...
Ross: Baby got back (Then Ross turns around and sees their friends standing in the doorway)
Rachel: One more time from the top...  (She also turns and sees the gang)
Ross: Rachel please! That is so inappropriate!
END


Wikidata is a free and open knowledge base that can be read and edited by both humans and machines. Wikidata acts as central storage for the structured data of its Wikimedia sister projects including Wikipedia, Wikivoyage, Wiktionary, Wikisource, and others.
The Wikidata repository consists mainly of items, each one having a label, a description and any number of aliases. Items are uniquely identified by a Q followed by a number, such as Douglas Adams (Q42). Statements describe detailed characteristics of an Item and consist of a property and a value. Properties in Wikidata have a P followed by a number, such as with educated at (P69).
WikiTimeline is a tool which retrieves data from the user about a particular entity and obtain facts about that entity from the WIKIDATA database. Using these facts and information, we develop a timeline graph and display on the graph.
It also allows allow the users to access various relationships of the entity and navigate from one entity to another.
Click on the "Start with something fun" button above. Try out the functions on our example timeline.
WikiTimeline application is an open-source & free application and may be modified to the needs of the user as long as the user adheres to MIT license. Any one can contribute towards this project or build upon the existing project. You can find us on GitHub.
Here is a short video instruction on how to explore our WikiTimeline.
Explore more with your creativity.


|
GLAC
1.0
|
Class for holding \(\mathrm{SU}(2)\) matrices. More...
#include <su2.h>
Public Member Functions | |
| SU2 () | |
| ~SU2 () | |
| void | print () |
| void | zeros () |
| void | identity () |
| void | setComplex (complex w, int i) |
| SU2::setComplex. More... | |
| double | normSquared (int i) |
| SU2 | transpose () |
| SU2 | conjugate () |
| SU2 | inv () |
| SU2::inv. More... | |
| double | get (int i, int j, int k) |
| double & | operator[] (int i) |
| SU2 & | operator= (const SU2 &B) |
| SU2 & | operator+= (SU2 B) |
| SU2 & | operator-= (SU2 B) |
| SU2 & | operator *= (SU2 B) |
| SU2 & | operator *= (double B) |
Data Fields | |
| double | mat [8] |
Class for holding \(\mathrm{SU}(2)\) matrices.
Operations have been overloaded for ease of use.
|
inline |
|
inline |
| SU2 SU2::conjugate | ( | ) |
|
inline |
|
inline |
| double SU2::normSquared | ( | int | i | ) |
|
inline |
|
inline |
| void SU2::print | ( | ) |
| void SU2::setComplex | ( | complex | w, |
| int | i | ||
| ) |
| w | complex number |
| i | matrix position(contigious) to set w as. |
| SU2 SU2::transpose | ( | ) |
|
inline |
| double SU2::mat[8] |


|
package com.alpha.activities.listeners;
import android.app.Activity;
import android.app.Application;
import android.content.ComponentName;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.IBinder;
import com.alpha.activities.EyesOnTeacherActivity;
import com.alpha.activities.LockedActivity;
import com.alpha.activities.NotebooksActivity;
import com.alpha.activities.StudentNotebookActivity;
import com.alpha.api.messaging.StudentLocation;
import com.alpha.api.services.ApplicationMonitoringService;
import com.alpha.api.services.ApplicationMonitoringServiceConnection;
import com.alpha.api.services.events.ConnectionClosedEvent;
import com.alpha.api.services.events.JoinSessionEvent;
import com.alpha.api.services.events.StudentSessionEndedEvent;
import com.alpha.logging.LogUtils;
import com.alpha.utils.ApplicationUtils;
import com.google.common.eventbus.EventBus;
import com.google.common.eventbus.Subscribe;
import com.google.inject.Inject;
import org.slf4j.Logger;
public class StudentNotebookStateMonitor implements Application.ActivityLifecycleCallbacks, ServiceConnection {
private static final Logger LOG = LogUtils.getLogger();
private final ApplicationUtils applicationUtils;
private final ApplicationMonitoringServiceConnection serviceConnection;
Listeners listeners = new Listeners();
protected StudentLocation lastKnownLocation = StudentLocation.HOME_SCREEN;
@Inject
public StudentNotebookStateMonitor(Application application, ApplicationUtils applicationUtils, EventBus eventBus, ApplicationMonitoringServiceConnection serviceConnection) {
this.applicationUtils = applicationUtils;
this.serviceConnection = serviceConnection;
serviceConnection.setConnectionListener(this);
// We always need lifecycle callbacks so we can send an
// accurate location when student eventually joins session
application.registerActivityLifecycleCallbacks(this);
// Doesn't need to be unregistered, assuming that this class is a
// singleton that exists throughout the application's lifetime
eventBus.register(listeners);
}
@Override
|
| Duplicate |
public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
}
|
|
@Override
|
| Duplicate |
public void onActivityStarted(Activity activity) {
}
|
|
@Override
public void onActivityResumed(Activity activity) {
if (activity instanceof NotebooksActivity) {
lastKnownLocation = StudentLocation.HOME_SCREEN;
} else if (activity instanceof EyesOnTeacherActivity) {
lastKnownLocation = StudentLocation.EYES_ON_TEACHER;
} else if ((activity instanceof StudentNotebookActivity) || (activity instanceof LockedActivity)) {
lastKnownLocation = StudentLocation.SECTION;
}
tryUpdateServiceLocation(lastKnownLocation);
}
@Override
|
| Duplicate |
public void onActivityPaused(Activity activity) {
}
|
|
@Override
public void onActivityStopped(Activity activity) {
|
| Duplicate |
if (!applicationUtils.isApplicationSentToBackground(activity)) {
return;
}
|
|
lastKnownLocation = StudentLocation.ANOTHER_APPLICATION;
tryUpdateServiceLocation(lastKnownLocation);
}
@Override
|
| Duplicate |
public void onActivitySaveInstanceState(Activity activity, Bundle outState) {
}
|
|
@Override
|
| Duplicate |
public void onActivityDestroyed(Activity activity) {
}
|
|
private void tryUpdateServiceLocation(StudentLocation location) {
|
| Duplicate |
if( !serviceConnection.stillBoundToService() ) {
return;
}
|
|
ApplicationMonitoringService service = serviceConnection.getService();
service.updateLocation(location);
}
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
tryUpdateServiceLocation(lastKnownLocation);
}
@Override
|
| Duplicate |
public void onServiceDisconnected(ComponentName name) {
// Do nothing
}
|
|
class Listeners {
@Subscribe
public void onSessionJoinEvent(JoinSessionEvent event) {
LOG.info("Joined a session, opening connection to monitoring service");
serviceConnection.open();
}
@Subscribe
public void onSessionEndEvent(StudentSessionEndedEvent event) {
LOG.info("Session ended, closing connection to monitoring service");
serviceConnection.close();
}
@Subscribe
public void onConnectionClosedEvent(ConnectionClosedEvent e){
LOG.info("Connection closed, closing connection to monitoring service");
serviceConnection.close();
}
}
}
|