GOF - The hidden pattern.
03 Oct 2017The Gang-Of-Four book has 24 patterns. And it only took me only about 12 years or so to get it, considering that I first encountered the book in 2005.
I know, I know. I can hear the collective tirade of protest.
All the analysis and the literature says 23.
Anyone who can count to 23 says 23.
The number of pattern descriptions in the book are 23.
In this case at least, as far as common knowledge goes, 23 is the answer, not 42.
Yet I will argue. There is another pattern in there. A pattern that is repeated over and over.
You disagree, eh? Ok then, we'll try a different tack.
Pattern No. 24. An oblique introduction.
Are you familiar with the word 'concept'? Yes. Good. Thank you. A no here would have made it really difficult to proceed.
So you have maybe a concept or two of your own. Yes, Even better.
Now here's the kicker - explain your concept to me, in writing. (It doesn't have to be technical even. It could be in woodworking for all I care). But try writing down the concept.
Yes, go right ahead.  I can wait. See I have coffee, blogs, white-noise. I am all good with waiting a bit.
Writing down a concept is not an uncommon activity in the software engineering line of business. We are asked to explain concepts all of the while, in writing - RFP submissions, software design documents, new initiative proposals, POC summaries. I have done my bit.
Recently I did one more - A 'starting devOps' analysis. And it was while doing this that I also started to think about 'how to explain' such a thing to someone else. And I found that everytime I need to explain a concept, I tend to use the same patterns to do so. It was a curious co-incidence that design pattern fever was running rather high at the workplace at that period, and recurring discussions were happening around this famous book and its contents.
Explaining yourself, preferably clearly.
So a quick look at what I do when writing down a concept?
Step 1 - Motivation
This part defines the motivations and reasons for the concept. In my case, It has traditionally involved consideration and listing down of the following
- The driving forces
- The constraining forces
- The goals
Step 2 - Drivers and Constraints
Given the drivers, constraints and goals, I now proceed to
- An explanation of the general structure of the concept.
Step 3 - Structure
Given the drivers, and the structure, we now drill down further into
- Key stake-holders
- Key components involved in the concept,
- Key interactions between components themselves or with a stakeholder, all being traceable to one or more of the motivations.
Step 4 - Downsides and Limitations
The next step is usually to play devils advocate. As such, we make a listing of
- Assumptions
- Tradeoffs
- Areas of applicability
Step 5 - References
We are now reaching the close of the explanation. At this point, we should be citing
- References
- Documentation for further details.
Step 6 - Risks, Mitigations and Alternatives
No concept paper is complete without a brief note about cases which have a high chance of rendering the current concept invalid and leads us to look for alternative solutions. So we note down the
- Limitations of concept
- Alternative possibilities.
Hopefully still here with me. And not too many objections. Ok.
Now I will copy a passage from a textbook. The following section is a verbatim copy-paste from the GOF book
Describing Design Patterns
How do we describe design patterns? Graphical notations, while important and useful, aren't sufficient. They simply capture the end product of the design process as relationships between classes and objects. To reuse the design, we must also record the decisions, alternatives, and trade-offs that led to it. Concrete examples are important too, because they help you see the design in action.
We describe design patterns using a consistent format. Each pattern is divided into sections according to the following template. The template lends a uniform structure to the information, making design patterns easier to learn, compare, and use.
Pattern Name and Classification
The pattern's name conveys the essence of the pattern succinctly. A good name is vital, because it will become part of your design vocabulary. The pattern's classification reflects the scheme we introduce in Section 1.5.
Intent
A short statement that answers the following questions: What does the design pattern do? What is its rationale and intent? What particular design issue or problem does it address?
Also Known As
Other well-known names for the pattern, if any.
Motivation
A scenario that illustrates a design problem and how the class and object structures in the pattern solve the problem. The scenario will help you understand the more abstract description of the pattern that follows.
Applicability
What are the situations in which the design pattern can be applied? What are examples of poor designs that the pattern can address? How can you recognize these situations?
Structure
A graphical representation of the classes in the pattern using a notation based on the Object Modeling Technique (OMT) [RBP+91]. We also use interaction diagrams [JCJO92, Boo94] to illustrate sequences of requests and collaborations between objects. Appendix B describes these notations in detail.
Participants
The classes and/or objects participating in the design pattern and their responsibilities.
Collaborations
How the participants collaborate to carry out their responsibilities.
Consequences
How does the pattern support its objectives? What are the trade-offs and results of using the pattern? What aspect of system structure does it let you vary independently?
Implementation
What pitfalls, hints, or techniques should you be aware of when implementing the pattern? Are there language-specific issues?
Sample Code
Code fragments that illustrate how you might implement the pattern in C++ or Smalltalk.
Known Uses
Examples of the pattern found in real systems. We include at least two examples from different domains.
Related Patterns
What design patterns are closely related to this one? What are the important differences? With which other patterns should this one be used?
The appendices provide background information that will help you understand the patterns and the discussions surrounding them. Appendix A is a glossary of terminology we use. We've already mentioned Appendix B, which presents the various notations. We'll also describe aspects of the notations as we introduce them in the upcoming discussions. Finally, Appendix C contains source code for the foundation classes we use in code samples.
The hidden pattern, finally
And here we are. The approach taken to describe the Patterns in the first place.
It is repeated 23 times in the book. In each chapter, to be precise. If you read the book thoroughly, not only do you understand specific patterns in full, but you learn how to analyse and understand patterns. It is similiar to learning algorithms, but it is NOT just about learning algorithms, but learning how to analyse them as well.
The outline presented here is basically what I fall back to everytime I need to explain a concept. And like patterns itself, I use some of it, all of it, or in combination with something else, but the concept remains.
Thus, Ladies and Gentlemen, Geeks and Nerds, I give you the GOF Pattern No. 24.
The 'Concept Elucidation Pattern'.
All yours, now.


The JS9 Public API
The JS9 Public API
The JS9 Public API provides a JavaScript programming interface for
interacting with JS9. Obviously, all of JS9 JavaScript code is
available to you within a Web page, but the public API is designed to
stable and well-documented for users and Web designers. It also will
provide the basis for planned language bindings (python, perhaps C/Fortran).
In general, the public API routines act on the current image being
displayed in the default JS9 div element. Since most Web pages will
have only a single JS9 div element, this behavior is usually what you
want. It parallels DS9's behavior in which XPA commands act on the
currently displayed image. For example, to change the colormap of the
current image, use:
JS9.SetColormap("cool");
However, for cases where multiple JS9 div elements are defined on a
single page, you can specify the specific div element to process by
adding an display object argument to the calling sequence with
a single display property:
{display: [div_id]}
where [div_id] is the id of the target JS9 div element. For example,
if two JS9 divs with ids "JS9" and "myJS9", respectively, are present
on a single Web page, you can set the colormap of the second one this way:
JS9.SetColormap("cool", {display: "myJS9"})
Note that this
display object contains only the
display
property and always is specified as the final argument in a call.
The display property can specify an image handle instead of
a JS9 div element id. This is a minor optimization for cases where you want
to call a routine such as JS9.WCSToPix() or JS9.PixtoWCS() many times:
var im = JS9.GetImage({display: "myJS9"});
for(i=0; i<1000; i++){
x = ...
y = ...
wcs = JS9.PixtoWCS(x, y, {display: im});
console.log("%s %s -> %s %s", x, y, wcs.ra, wcs.dec);
}
Passing the image handle will speed up processing very slightly, but
it makes us programmers feel much better.
The optional display object argument is not explicitly
documented in the routines below (except as an example
in JS9.Load()), but it is always available.
Choose from the following sections:
Loading Images
● Load an image into JS9
JS9.Load(url, opts, func)
where:
- url: url to load
- opts: object containing image parameters
- func: optional function to call when image load is complete
Load a FITS file or a PNG representation file into JS9.
To override default image parameters, pass the image opts argument:
href='javascript:JS9.Load("png/m13.png", {scale:"linear", colormap:"sls"});'
If a callback function is specified, it will be called after the image
is loaded:
href='javascript:JS9.Load("fits/3c273.fits", {scale: "linear"}, func);'
The image handle is passed as the first argument to the callback.
To load an image into a specified display, pass the display object as the last argument:
href='javascript:JS9.Load("fits/3c273.fits", {scale: "linear"}, {display: "myJS9"});'
See Displaying Your Data for further discussion of
how to use this routine.
● Load an image into a light window or a new (separate) window
JS9.LoadWindow(url, opts, type, html, winopts)
where:
- url: url to load
- opts: optional object containing image parameters
- type: "light" or "new"
- html: html for the new page (default is to display a menubar above the image)
- winopts: for "light", optional dhtml window options
This routine will load an image into a light-weight window or an entirely new
window. The
url and
opts arguments are identical to the standard
JS9.Load() call. The
type argument determines whether to create
a light-weight window or a new (separate) window.
By default, the new window will contain a Menubar above an image display area:

You can use the
html argument to supply different Web page elements
for the window. Furthermore, if you create a light window, a default
set of DynamicDrive dhtmlwindow parameters will be used to make the
window the correct size for the default html:
"width=512px,height=542px,center=1,resize=1,scrolling=1"
You can supply your own parameters for the new dhtmlwindow using the
winOpts argument. See:
DynamicDrive for more information about their light-weight window.
See js9create.html for examples of how to use this routine.
● Load one or more images when the Web page is ready
JS9.Preload(url1, opts1, url2, opts2, ... url2, optsn)
where:
- url: url to load
- opts: optional object containing image parameters
This routine will pre-load images into a JS9 display. It can be added
to the Web page body element using the onload() JavaScript call or
called in a JavaScript init routine tied to onload. See index.html and
js9preload.html for examples.
It is worth emphasizing that JS9.Preload() should not be called
until the Web page is fully loaded, since JS9 itself is not fully
initialized until then. It is for this reason that JS9.Preload()
generally is called using an onload routine tied to the Web page body.
Working with Images
● Get image handle for the current image
im = JS9.GetImage()
The routine returns the image handle associated with the current image.
The returned image handle can be passed in the display object. This
is marginally more efficient than the default behavior, which is to
determine the current image for each call.
● Get image data and auxiliary info for the specified image
imgdata = JS9.GetImageData(dflag)
where:
- dflag: true if you want the data returned (as an HTML5 typed array)
The image data object contains the following information:
- id: the id of the file that was loaded into JS9
- file: the file or URL that was loaded into JS9
- source: "fits" if a FITS file was downloaded, "fits2png" if a representation file was retrieved
- imtab: "image" for FITS images and png files, "table" for FITS binary tables
- width: x dimension of image
- height: y dimension of image
- bitpix: FITS bits/pixel of each image element (8 for unsigned char, 16, 32 for signed integer, -32 or -64 for float)
- header: JavaScript object containing FITS header values
- data: if dflag is true, a pointer to HTML5 typed array
containing image data
This call returns raw data for subsequent use in local analysis tasks. The data
buffer is a reference to the real data, so be careful about changing values.
Given a FITS-standard 1-indexed image pixel x,y, you can find the data value
at that location using:
val = obj.data[Math.floor(y-0.5) * obj.width + Math.floor(x-0.5)];
Note the need to integerize the x and y values: JavaScript arrays are
objects and so floating point array indices do not get truncated
automatically as in C. They will return null values.
Also note that since we need 0-based data array indexes, we subtract 1 from
the 1-based image position. But then we must add 0.5 before rounding because
by convention, x.0, y.0 is the middle of the pixel.
● Display an image
JS9.DisplayImage(step)
where:
- step: starting step to take when displaying the image
The display steps are: "colors" (remake color data when cmap has changed),
"scaled" (rescale data values), "primary" (convert scaled data values
to color values), and "display" (write color values to the Web page).
The default step is "primary", which displays the image without recalculating
color data, scaled data, etc. This generally is what you want, unless you have
explicitly changed parameter(s) used in a prior step.
● Re-read the data from a FITS file and re-display
JS9.RefreshImage(fitsobj, func)
where:
- fitsobj: new FITS object from which to take raw data for display
- func: optional function to call when image load is complete
This routine can be used, for example, in laboratory settings where
data is being gathered in real-time and the JS9 display needs to be
refreshed periodically. The call takes a fits object as the second
argument. It will refresh the display using the new data contained in that
object.
The fits object should contains the following properties:
- naxis: number of axes in the image
- axis: array of image dimensions for each axis
- bitpix: FITS bitpix value
- image: typed data array containing current image data
- head: object containing header keywords as properties
- card: string version of the header with comments (optional)
- dmin: data min (optional)
- dmax: data max (optional)
● Clear the image from the display and mark resources for release
JS9.CloseImage()
Each loaded image claims a non-trivial amount of memory from a finite
amount of browser heap space. For example, the default 32-bit version
of Google Chrome has a memory limit of approximately 500Mb. If you are
finished viewing an image, closing it tells the browser that the
image's memory can be freed. In principle, this is can help reduce
overall memory usage as successive images are loaded and
discarded. Note, however, that closing an image only provides a hint
to the browser, since this sort of garbage collection is not directly
accessible to JavaScript programming.
Some day, all browsers will support full 64-bit addressing and this problem will
go away ...
● Get the image colormap
cmap = JS9.GetColormap()
returns:
- cmap: string containing colormap name
Get the current colormap name.
● Set the image colormap
JS9.SetColormap(cmap, [contrast, bias])
where:
- cmap: colormap name
- contrast: contrast value (range: 0 to 10)
- bias: bias value (range 0 to 1)
Set the current colormap, contrast/bias, or both. This call takes one
(colormap), two (contrast, bias) or three (colormap, contrast, bias)
arguments.
● Get the image scale
scale = JS9.GetScale()
returns:
- scale: string containing scale name
Get the current scale name.
● Set the image scale
JS9.SetScale(scale, smin, smax)
where:
- scale: scale name
- smin: scale min value
- smax: scale max value
Set the current scale, min/max, or both. This call takes one
(scale), two (smin, max) or three (scale, smin, smax) arguments.
● Get the image zoom factor
zoom = JS9.GetZoom()
returns:
- zoom: floating point zoom factor
Get the zoom factor.
● Set the image zoom factor
JS9.SetZoom(zoom)
where:
- zoom: floating or integer zoom factor or zoom directive string
The zoom directives are:
- x[n]|X[n]: multiply the zoom by n (e.g. "x2")
- /[n]: divide the zoom by n (e.g. "/2")
- in|In: zoom in by a factor of two
- out|Out: zoom out by a factor of two
- toFit|ToFit: zoom to fit image in display
● Get the image pan position
ipos = JS9.GetPan()
returns:
- ipos: image position object containing x and y image
coordinate values
Get the current pan position in image coordinates.
● Set the image pan position
JS9.SetPan(x, y)
where:
- x: x image coordinate
- y: y image coordinate
Set the current pan position using image coordinates.
Note that you can use
JS9.WCSToPix() and
JS9.PixToWCS() to
convert between image and WCS coordinates.
● Get the display coordinates from an event
dpos = JS9.EventToDisplayPos(evt)
where:
returns:
- dpos: display position object containing x and y display
coordinate values
If you define your own event callbacks, you can use this routine to convert
the event position to a display position, which can then be used to get the
image position (see below).
● Get the image coordinates from the display coordinates
ipos = JS9.DisplayToImagePos(dpos)
where:
- dpos: display position object containing x and y display
coordinate values
returns:
- ipos: image position object containing x and y image
coordinate values
Note that image coordinates are one-indexed, as per FITS conventions, while
display coordinate are 0-indexed.
● Get the display coordinates from the image coordinates
dpos = JS9.ImageToDisplayPos(ipos)
where:
- ipos: image position object containing x and y image
coordinate values
returns:
- dpos: display position object containing x and y display
coordinate values
Get display (screen) coordinates from image coordinates. Note that
image coordinates are one-indexed, as per FITS conventions, while
display coordinate are 0-indexed.
● Update value/position information
valpos = JS9.UpdateValpos(ipos, display)
where:
- ipos: image position object containing x and y image
coordinate values
- display: boolean value specifying whether the info box display should be updated (default is true)
This routine determines the data value at a given image position
and returns an object containing the following information:
- ix: image x coordinate
- iy: image y coordinate
- isys: image system (i.e. "image")
- px: physical x coordinate
- py: physical y coordinate
- psys: currently selected pixel-based system (i.e. "image"
or "physical") for the above px, py values
- ra: ra in degrees (if WCS is available)
- dec: dec in degrees (if WCS is available)
- wcssys: wcs system (if WCS is available)
- val: floating point pixel value
- val3: pixel value as a string truncated to 3 decimal digits
- vstr: string containing value and position info
- id: id of the image
- file: filename of the image
- object: object name of the image from the FITS header
● Get the current WCS units
unitsstr = JS9.GetWCSUnits()
returns:
- unitstr: "pixels", "degrees" or "sexagesimal"
Get the current WCS units.
● Set the current WCS units
JS9.SetWCSUnits(unitsstr)
where:
- unitstr: "pixels", "degrees" or "sexagesimal"
Set the current WCS units.
● Get the current World Coordinate System
sysstr = JS9.GetWCSSys()
returns:
- sysstr: current World Coordinate System ("FK4", "FK5", "ICRS",
"galactic", "ecliptic", "image", or "physical");
Get current WCS system.
● Set the current World Coordinate System
JS9.SetWCSSys(sysstr)
where:
- sysstr: World Coordinate System ("FK4", "FK5", "ICRS",
"galactic", "ecliptic", "image", or "physical")
Set current WCS system. The WCS systems are available only if WCS
information is contained in the FITS header. Also note that "physical"
coordinates are the coordinates tied to the original file. They are
mainly used in X-ray astronomy where individually detected photon
events are binned into an image, possibly using a blocking factor.
For optical images, image and physical coordinate usually are identical.
● Convert image pixel position to WCS position
wcsobj = JS9.PixToWCS(x, y)
where:
- x: x image coordinate
- y: y image coordinate
returns:
- wcsobj: world coordinate system object
The wcs object contains the following properties:
- ra: right ascension in floating point degrees
- dec: declination in floating point degrees
- sys: current world coordinate system being used
- str: string of wcs in current system ("[ra] [dec] [sys]")
● Convert WCS position to image pixel position
pixobj = JS9.WCSToPix(ra, dec)
where:
- ra: right ascension in floating point degrees
- dec: declination in floating point degrees
returns:
The pixel object contains the following properties:
- x: x image coordinate
- y: y image coordinate
- str: string of pixel values ("[x]" "[y]")
Working with Regions
Spatial regions of interest are a crucial part of astronomical data
analysis. The regions layer is a special case of the more generalized
shape layers, automatically created by JS9 to support the options in
the Regions menu, as well as local and server-side data analysis
using regions.
The regions layer has the special property that, by default,
its z-index is higher than other shape layers, so that regions
are displayed on top of other shape layers.
Note that the GetRegions(), ChangeRegions(), RemoveRegions() calls
all take a regions specification as the second argument, which can be
any of the following (in order of precedence):
- "all": all regions
- [id]: a specific region id returned by JS9.AddRegions()
- "selected": the selected region (or all regions in the selected group)
- [color]: all regions having the specified color (e.g., "red")
- [shape]: all regions having the specified shape (e.g., "circle")
- [tag]: all regions containing the specified tag (e.g., "source")
Thus, it is possible to act on multiple regions at the same time.
● Add one or more regions to the regions layer
id = JS9.AddRegions(rarr, opts)
where:
- rarr: a shape string, region object or an array of region objects
- opts: global values to apply to each created region
returns:
- id: id of last region created
The
rarr argument can be a region shape ("annulus", "box",
"circle", "ellipse", "point", "polygon", "text"), a single region
object, or an array of region objects. Region objects contain one or
more properties, of which the most important are:
- shape: "annulus", "box", "circle", "ellipse", "point", "polygon", "text" [REQUIRED]
- x: image x position
- y: image y position
- lcs: object containing logical x, y and sys (e.g. "physical")
- dx: increment from current image x position
- dy: increment from current image y position
- tags: comma separated list of tag strings
- radii: array of radii for annulus region
- width: width for box region
- height: height for box region
- radius: radius value for circle region
- r1: x radius for ellipse region (misnomer noted)
- r2: y radius for ellipse region (misnomer noted)
- pts: array of objects containing x and y positions for polygons
- points: array of objects containing x and y offsets from the center for polygons
- angle: angle in degrees for box and ellipse regions
- color: region color (string name or #rrggbb syntax)
- text: text associated with text region
Other available properties include:
- fixinplace: if true, region cannot be moved or resized
- lockMovementX: region cannot be moved in the x direction
- lockMovementY: region cannot be moved in the y direction
- lockRotation: region cannot be rotated
- lockScalingX: region cannot be resized in the x direction
- lockScalingY: region cannot be resized in the y direction
- fontFamily: font parameter for text region
- fontSize: font parameter for text region
- fontStyle: font parameter for text region
- fontWeight: font parameter for text region
● Get information about one or more regions
rarr = JS9.GetRegions(regions)
where:
- regions: which regions to retrieve
returns:
- rarr: array of region objects
Each returned region object contains the following properties:
- id: numeric region id (assigned by JS9 automatically)
- mode: "add", "remove" or "change"
- shape: region shape ("annulus", "box", "circle", "ellipse",
"point", "polygon", "text")
- tags: comma delimited list of region tags (e.g., "source", "include")
- color: region color
- x,y: image coordinates of region
- radii: array of radii for annulus region
- width: width for box region
- height: height for box region
- radius: radius value for circle region
- r1: x radius for ellipse region (misnomer noted)
- r2: y radius for ellipse region (misnomer noted)
- pts: array of objects containing x and y positions, for polygons
- points: array of objects containing x and y offsets from the specified center, for polygons
- angle: angle in degrees for box and ellipse regions
- wcsstr: region string in wcs coordinates
- wcssys: wcs system (e.g. "FK5")
- imstr: region string in image or physical coordinates
- imsys: image system ("image" or "physical")
The image position can be used to access the image data returned by the
JS9.GetImageData() routine:
obj = JS9.GetImageData();
xreg = JS9.GetRegions("selected")[0];
val = obj.data[Math.floor(xreg.y-0.5) * obj.width + Math.floor(xreg.x-0.5)];
Note the need to integerize the x and y values: JavaScript arrays are
objects and so floating point array indices do not get truncated
automatically as in C. They will return null values.
Also note that since we need 0-based data array indexes, we subtract 1 from
the 1-based image position. But then we must add 0.5 before rounding because
by convention, x.0, y.0 is the middle of the pixel.
● Change one or more regions
JS9.ChangeRegions(regions, opts)
where:
- regions: which regions to change
- opts: object containing options to change in each region
Change one or more regions. The opts object can contain the parameters
described in the
JS9.AddRegions() section. However, you cannot (yet)
change the shape itself (e.g. from "box" to "circle"). See js9onchange.html
for examples of how to use this routine.
● Remove one or more regions from the region layer
JS9.RemoveRegions(regions)
where:
- regions: which regions to remove
Working with Shape Layers
JS9 supports individual layers for drawing 2D graphics. The regions
layer is a special case of a shape layer, created automatically by
JS9. The Catalog plugin creates a separate layer for each catalog.
You can define your own shape layer using the NewShapeLayer()
call and then add geometric shapes to it.
Note that the JS9.GetShapes(), JS9.ChangeShapes(), JS9.RemoveShapes()
calls all take a shape specification as the second argument, which can
be any of the following (in order of precedence):
- "all": all regions
- [id]: a specific region id returned by JS9.AddRegions()
- "selected": the selected region (or all regions in the selected group)
- [color]: all regions having the specified color (e.g., "red")
- [shape]: all regions having the specified shape (e.g., "circle")
- [tag]: all regions containing the specified tag (e.g., "source")
Thus, it is possible to act on multiple shapes at the same time.
● Create a new shape layer
lid = JS9.NewShapeLayer(layer, opts)
where:
- layer: name of the layer to create
- opts: default options for this layer
returns:
This routine creates a new named shape layer. You can then, add, change,
and remove shapes in this layer using the routines below. The catalogs
displayed by the Catalog plugin are examples of separate shape layers.
The optional opts parameter allows you to specify default
options for this layer. You can set a default for any property needed
by your shape layer. See JS9.Regions.opts in js9.js for an example of
the default options for the regions layer.
● Show or hide the specified shape layer
JS9.ShowShapeLayer(layer, mode)
where:
- layer: name of layer
- mode: true (show layer) or false (hide layer)
Shape layers can be hidden from display. This could be useful, for
example, if you have several catalogs loaded into a display and
want to view one at a time.
● Add one or more shapes to the specified layer
JS9.AddShapes(layer, sarr, opts)
where:
- layer: name of layer
- sarr: a shape string, shape object, or an array of shape objects
- opts: global values to apply to each created shape
returns:
- id: id of last shape created
The
sarr argument can be a shape ("annulus", "box",
"circle", "ellipse", "point", "polygon", "text"), a single shape
object, or an array of shape objects. Shape objects contain one or
more properties, of which the most important are:
- shape: "annulus", "box", "circle", "ellipse", "point", "polygon", "text" [REQUIRED]
- x: image x position
- y: image y position
- dx: increment from current image x position
- dy: increment from current image y position
- tags: comma separated list of tag strings
- radii: array of radii for annulus shape
- width: width for box shape
- height: height for box shape
- radius: radius value for circle shape
- r1: x radius for ellipse shape (misnomer noted)
- r2: y radius for ellipse shape (misnomer noted)
- pts: array of objects containing x and y positions, for polygons
- points: array of objects containing x and y offsets from the specified center, for polygons
- angle: angle in degrees for box and ellipse shapes
- color: shape color (string name or #rrggbb syntax)
- text: text associated with text shape
Other available properties include:
- fixinplace: if true, shape cannot be moved or resized
- lockMovementX: shape cannot be moved in the x direction
- lockMovementY: shape cannot be moved in the y direction
- lockRotation: shape cannot be rotated
- lockScalingX: shape cannot be resized in the x direction
- lockScalingY: shape cannot be resized in the y direction
- fontFamily: font parameter for text shape
- fontSize: font parameter for text shape
- fontStyle: font parameter for text shape
- fontWeight: font parameter for text shape
● Remove one or more shapes from the specified shape layer
JS9.RemoveShapes(layer, shapes)
where:
- layer: name of layer
- shapes: which shapes to remove
● Get information about one or more shapes in the specified shape layer
JS9.GetShapes(layer, shapes)
where:
- layer: name of layer
- shapes: which shapes to retrieve
returns:
- sarr: array of shape objects
Each returned shape object contains the following properties:
- id: numeric region id (assigned by JS9 automatically)
- mode: "add", "remove", or "change"
- shape: region shape ("annulus", "box", "circle", "ellipse",
"point", "polygon", "text")
- tags: comma delimited list of region tags (e.g., "source", "include")
- color: region color
- x,y: image coordinates of region
- size: object containing width and height for box region
- radius: radius value for circle region
- radii: array of radii for annulus region
- eradius: object containing x and y radii for ellipse regions
- pts: array of objects containing x and y positions, for polygons
- angle: angle in degrees for box and ellipse regions
● Change one or more shapes in the specified layer
JS9.ChangeShapes(layer, shapes, opts)
where:
- layer: name of layer
- shapes: which shapes to change
- opts: object containing options to change in each shape
Change one or more shapes. The opts object can contain the parameters
described in the
JS9.AddShapes() section. However, you cannot (yet)
change the shape itself (e.g. from "box" to "circle").
Server-side Analysis
● Run a simple server-side analysis task
JS9.RunAnalysis(aname, parr, func)
where:
- aname: name of analysis tool
- parr: optional array of macro-expansion options for command line
- func: optional routine to call when analysis is completed on server
The
JS9.RunAnalysis() routine is used to execute a server-side
analysis task and return the results for further processing within the
Web page.
The optional parr array of parameters is passed to the JS9
analysis macro expander so that values can be added to the command
line. The array is in jQuery name/value serialized object format,
which is described here:
http://api.jquery.com/serializeArray/
func: a function to process the returned data when the task is complete.
The func() routine is a callback function to process the returned
results from the analysis task. The calling sequence is:
func(stdout, stderr, errcode, aobj)
where:
- stdout: a string containing the standard output from the task
- stderr: a string containing the standard error from the task
- errcode: error code value from the task or 0
- aobj: the analysis object from the js9Analysis.json file,
containing, among other things, the important rtype:
"text" or "plot"
Typically, you would check stderr string first and issue an error
message if there is an error. Otherwise, the stdout string can be
processed based on the return type (rtype) property of the output
(e.g., "text" or "plot"). For plotting, you can use flot
functionality already loaded into JS9, or you can use your own chosen
plotting package.
If no func callback is specified, the default processing will
display "text" in a new light window. If the return type is "plot",
the results are assumed to be in flot format and will be plotted.
● Run a server-side analysis task utilizing
parameters in a form
JS9.SubmitAnalysis(el, aname, func)
where:
- el: form element (unused at present)
- aname: name of analysis tool
- func: optional routine to call when analysis is completed on server
The JS9.SubmitAnalysis() routine is used to run an analysis
task with input parameters from a form. Typically used as
the Run button action in a form, it automatically serializes
the form values and passes them to the JS9 analysis macro expander so
that these values can be integrated into the analysis command line.
See js9analysis.html for a simple example.
The func callback is the same as for JS9.RunAnalysis() above.
Miscellaneous
● Print an image
JS9.Print()
Print the currently displayed image. A new window is displayed
containing the image, along with regions and other graphical layers
(the 2D graphics having been converted to a re-scalable format). The
standard Print dialog box also is displayed and can be used to print
this new window. Dismiss both windows when you are finished.
● Display help in a light window
JS9.DisplayHelp(name)
where:
- name: name of a help file or url of a Web site to display
The help file names are the property names in JS9.helpOpts (e.g., "user" for
the user page, "install" for the install page, etc.). Alternatively, you can
specify an arbitrary URL to display (just because).
● Get location of JS9 installation directory
rpath = JS9.InstallDir(file)
where:
- file: file or directory to convert
returns:
- rpath: path of the file relative to the JS9 install directory
Sometimes a plugin needs to load an auxiliary file inside the plugins
sub-directory. The Web page loading the plugin has an arbitrary
location relative to the JS9 install directory, so this routine
returns a relative path to the js9 install directory.
● Send a message to a back-end server
JS9.Send(msg, obj, cb)
where:
- msg: name of the msg, as defined in the back-end server
- obj: object containing msg parameters
- cb: callback to call when back-end server returns its results
JS9 sends various internal messages to the back-end server using either
socket.io protocol (in the Node.js implementation) or CGI. For
example, when a remote analysis call is made, JS9 sends a message to the
back-end server detailing the task to call, parameters to pass, etc. Results
are then returned to JS9 for display. JS9 also sends messages to the
back-end server when a new image is displayed, when a FITS file is to
be converted into a PNG representation file, etc.
Communication with the back-end is usually done behind the scenes and
need not concern users or application programmers. However, if you
write your own socket.io-based server, you might want to add
project-specific messages to your implementation. For example, a
Perl-based or Python-based server might add its own special messages
that execute Perl or Python commands within the server in response to
JS9 messages. In this case, you can use JS9.Send() to send a
message to these message handlers.
The msg name is the name of the message, as defined by the
server. By convention, an object is usually passed to the
message handler. JS9 will add a dataPath property to this
object to indicate the current list of directories in which to
search for data. All other properties are specific to the message being handled.
You can pass a null instead of an object and JS9.Send() will generate
a temporary object to hold the dataPath.
The cb function will be called if the message sends an
acknowledgment. The arguments passed to this function call by the server
are specific to the message being handled.
For example, you can send a message to the back-end server to retrieve the
list of available analysis tasks and then display this list using the call:
JS9.Send("getAnalysis", null, function(s){alert(s)});
The "getAnalysis" message passes no parameters to the server. The
server returns a list of available analysis tasks in JSON format.
● Add a JS9 display div and/or associated plugins
JS9.AddDivs(id1, id2, ...)
where:
- id1, id2, ...: optional name(s) of new JS9 display div(s)
You can add new JS9 displays and/or plugins dynamically. To do this, you
create the new divs, add them to the Web page, and then call this routine to
incorporate them into JS9.
The routine will accept a list of JS9 display divs to initialize. If
all you are doing is adding one or more plugins to an existing display,
leave the argument list empty.
For example, to add a new JS9 display and menubar at the end of a Web
page and load an image into that display:
var html = "

";
// jquery append to end of page
$(html).appendTo($("body"));
// create the new JS9 display, with associated plugins
JS9.AddDivs("myJS9");
// just a standard load to that display
JS9.Load("foo.fits", {scale: "log"}, {display: "myJS9"});
See also JS9.LoadWindow() for a nice way to load images into a
light-weight or completely new window.
Prototype Routines (not ready for prime time)
NB: The routines in this section are prototypes and therefore are
subject to change. Feel free to contact us to discuss your needs so
that we can gain a better understanding of is required in these cases.
● Load an auxiliary file
JS9.LoadAuxFile(name, func)
where:
- name: class name of auxiliary file to load
- func: function to call once the file is loaded
It sometimes is desirable to process auxiliary files when an image
is first loaded. Examples include overlaying an image mask on an
image, and pre-loading regions from a region file.
The JS9.LoadAuxFile prototypes this sort of functionality.
Auxiliary files are defined in the js9Prefs.json file by means of the
JS9.auxfiles array containing one or more auxfile objects. Each
auxfile specification has the following properties:
- type: type of auxiliary file: currently "mask" or "region"
- name: the class name of the auxiliary file
- image: image (or template) for which this auxfile is valid
- url: URL of the auxfile
For example, the following defines two auxfiles:
"auxFiles": [{"type": "mask",
"name": "sciencemasks",
"image": "casa.*.png",
"url": "../myauxfiles/casa-mask.png"},
{"type": "regions",
"name": "scienceregions",
"image": "casa.*.png",
"url": "../myauxfiles/casa.reg"}],
Here, a mask auxfile and a regions auxfile are defined for Cas-A
dataset(s). These auxfiles are brought into play as follows:
// run this routine after loading each image
function onImageLoad(im){
JS9.LoadAuxFile("sciencemasks", function(im, aux){
// if we succeed in loading the mask, set up the onchange callback
im.onregionschange = regionOnChange;
// view the image through the mask data
im.maskData = aux.im.raw.data;
// I mean now!
im.displayImage("all");
});
JS9.LoadAuxFile("scienceregions");
}
// tell JS9 about the onload callback
JS9.imageOpts.onload = onImageLoad;
An onImageLoad function is defined that will try to load auxfiles of
types "sciencemasks" and "scienceregions". This routine is set to
be called whenever a new image is loaded.
On image load, when JS9.LoadAuxFile() is called, it looks in
the list of known auxfiles for an entry that matches both
the class name and the image name. If found (in the example above,
for Cas-A datasets), the associated auxfile URL is loaded and
type-specific processing is performed:
- masks: the mask file is loaded into the Web page as an
off-screen image and an aux object is created as a property of the
image object. This aux object can be used for subsequent processing
(see below).
- regions: the regions in the region file are created and
displayed on the image.
Once the initial auxfile processing has been performed, the optional
callback function will be called. For the "mask" example above, the
onregionchange property is set to call a JavaScript function (not
shown here) whenever a region changes. The maskData property is set to
point to the raw data of the auxfile (i.e., aux.im.raw.data). Finally,
the image is redisplayed. Setting the maskData property and
redisplaying the image causes the auxfile's data to be displayed as an
opaque overlay on top of the currently displayed image.
As should be clear from the description above, considerable "black
magic" currently is involved when processing auxiliary files.
Visibility is required into the inner properties of the image
object. Please contact us if you want to work with auxiliary files, so
that we can continue to think about what is required for their support.
API Change History
The JS9 Public API is meant to be stable and well-documented. If we are
forced to make an incompatible change to the API, it will be documented here.
●
20141028: callback function to RunAnalysis and SubmitAnalysis
Due to an oversight, the signature of the callback function supplied to
RunAnalysis() (and its derivative function,
SubmitAnalysis()) was missing the errcode argument.
To correct this mistake, the signature was changed from:
func(stdout, stderr, aobj)
to:
func(stdout, stderr, errcode, aobj)
See js9onchange.html for an example of using this callback function.
Last updated: November 14, 2014


We’ll run for it,” Rand said. “Hurin, can you gallop and still follow the trail?”
“Yes, Lord Rand.”
“Then push on. We will—”
“It won’t do any good,” Selene said. Her white mare was the only one of their mounts not dancing at the gruff barks coming from the grolm. “They don’t give up, not ever. Once they have your scent, grolm keep coming, day and night, until they run you down.  Rand, the Portal Stone can take us elsewhere.”
“No!  I can. I already killed one. There are only five. If I can just find . . . ” He cast around for the spot he needed, and found it. “Follow me!” Digging his heels in, he set Red to a gallop, confident before he heard their hooves that the others would come.
The place he had chosen was a low, round hill, bare of trees. Nothing could come close without him seeing. He swung down from his saddle and unlimbered his longbow. Loial and Hurin joined him on the ground, the Ogier hefting his huge quarterstaff, the sniffer with his short sword in his fist. Neither quarterstaff nor sword would be of much use if the grolm closed with them. I won’t let them get close.
“This risk is not necessary,” Selene said. She barely looked toward the grolm, bending from her saddle to concentrate on Rand. “We can easily reach the Portal Stone ahead of them.”
“I will stop them.” Hastily Rand counted the arrows remaining in his quiver. Eighteen, each as long as his arm, ten of them with points like chisels, designed to drive through Trolloc armor. They would do as well for grolm as for Trollocs. He stuck four of those upright in the ground in front of him; a fifth he nocked to the bow. “Loial, Hurin, you can do no good down here. Mount and be ready to take Selene to the Stone if any get through.” He wondered whether he could kill one of the things with his sword, if it came to that. You are mad! Even the Power is not as bad as this.
Loial said something, but he did not hear; he was already seeking the void, as much to escape his own thoughts as for need. You know what’s waiting. But this way I don’t have to touch it. The glow was there, the light just out of sight. It seemed to flow toward him, but the emptiness was all. Thoughts darted across the surface of the void, visible in that tainted light. Saidin. The Power. Madness. Death. Extraneous thoughts. He was one with the bow, with the arrow, with the things topping the next rise.
The grolm came on, overreaching one another in their leaps, five great, leathery shapes, triple-eyed, with horny maws gaping. Their grunting calls rebounded from the void, barely heard.
Rand was not aware of raising his bow, or drawing the fletching against his cheek, to his ear. He was one with the beasts, one with the center eye of the first. Then the arrow was gone. The first grolm died; one of its companions leaped on it as it fell, beak of a mouth ripping gobbets of flesh. It snarled at the others, and they circled wide. But they came on, and as if compelled, it abandoned its meal and leaped after them, its horny maw already bloody.
Rand worked smoothly, unconsciously, nock and release. Nock and release.
The fifth arrow left his bow, and he lowered it, still deep in the void, as the fourth grolm fell like a huge puppet with its strings cut. Though the final arrow still flew, somehow he knew there was no need for another shot. The last beast collapsed as if its bones had melted, a feathered shaft jutting from its center eye. Always the center eye.
“Magnificent, Lord Rand,” Hurin said. “I . . . I’ve never seen shooting like that.”
The void held Rand. The light called to him, and he . . . reached . . . toward it. It surrounded him, filled him.
“Lord Rand?” Hurin touched his arm, and Rand gave a start, the emptiness filling up with what was around him. “Are you all right, my Lord?”
Rand brushed his forehead with fingertips. It was dry; he felt as if it should have been covered with sweat. “I . . . I’m fine, Hurin.”
“It grows easier each time you do it, I’ve heard,” Selene said. “The more you live in the Oneness, the easier.”
Rand glanced at her. “Well, I won’t need it again, not for a while.” What happened? I wanted to . . . He still wanted to, he realized with horror. He wanted to go back into the void, wanted to feel that light filling him again. It had seemed as if he were truly alive then, sickliness and all, and now was only an imitation. No, worse. He had been almost alive, knowing what “alive” would be like. All he had to do was reach out to saidin . . .
“Not again,” he muttered. He gazed off at the dead grolm, five monstrous shapes lying on the ground. Not dangerous anymore. “Now we can be on our—”
A coughing bark, all too familiar, sounded beyond the dead grolm, beyond the next hill, and others answered it. Still more came, from the east, from the west.
Rand half raised his bow.
“How many arrows do you have left?” Selene demanded.  Thirty? A hundred? We must go to the Portal Stone.”
“She is right, Rand,” Loial said slowly. “You do not have any choice now.” Hurin was watching Rand anxiously. The grolm called, a score of barks overlapping.
“The Stone,” Rand agreed reluctantly. Angrily he threw himself back into his saddle, slung the bow on his back. “Lead us to this Stone, Selene.”
With a nod she turned her mare and heeled it to a trot. Rand and the others followed, they eagerly, he holding back. The barks of grolm pursued them, hundreds it seemed. It sounded as if the grolm were ranged in a semicircle around them, closing in from every direction but the front.
Swiftly and surely Selene led them through the hills. The land rose in the beginning of mountains, slopes steepening so the horses scrambled over washed-out looking rocky outcrops and the sparse, faded-looking brush that clung to them. The way became harder, the land slanting more and more upward.
We’re not going to make it, Rand thought, the fifth time Red slipped and slid backwards in a shower of stone. Loial threw his quarterstaff aside; it would be of no use against grolm, and it only slowed him. The Ogier had given up riding; he used one hand to haul himself up, and pulled his tall horse behind him with the other. The hairy-fetlocked animal made heavy going, but easier than with Loial on its back. Grolm barked behind them, closer now.
Then Selene drew rein and pointed to a hollow nestled below them in the granite. It was all there, the seven wide, colored stairs around a pale floor, and the tall stone column in the middle.
She dismounted and led her mare into the hollow, down the stairs to the column. It loomed over her. She turned to look back up at Rand and the others. The grolm gave their grunting barks, scores of them, loud. Near. “They will be on us soon,” she said. “You must use the Stone, Rand. Or else find a way to kill all the grolm.”
With a sigh, Rand got down from his saddle and led Red into the hollow. Loial and Hurin followed hastily. He stared at the symbol-covered column, the Portal Stone, uneasily. She must be able to channel, even if she doesn’t know it, or it couldn’t have brought her here. The Power doesn’t harm women. “If this brought you here,” he began, but she interrupted him.
“I know what it is,” she said firmly, “but I do not know how to use it. You must do what must be done.” She traced one symbol, a little larger than the others, with a finger. A triangle standing on its point inside a circle. “This stands for the true world, our world. I believe it will help if you hold it in your mind while you . . . ” She spread her hands as if unsure exactly what it was he was supposed to do.
“Uh . . . my Lord?” Hurin said diffidently. “There isn’t much time.” He glanced over his shoulder at the rim of the hollow. The barking was louder. “Those things will be here in minutes, now.” Loial nodded.
Drawing a deep breath, Rand put his hand on the symbol Selene had pointed out. He looked at her to see if he was doing it right, but she merely watched, not even the slightest frown of worry wrinkling her pale forehead. She’s confident you can save her. You have to. The scent of her filled his nostrils.
“Uh . . . my Lord?”
Rand swallowed, and sought the void. It came easily, springing up around him without effort. Emptiness. Emptiness except for the light, wavering in a way that turned his stomach. Emptiness except for saidin. But even the queasiness was distant. He was one with the Portal Stone. The column felt smooth and slightly oily under his hand, but the triangle-and-circle seemed warm against the brand on his palm. Have to get them to safety. Have to get them home. The light drifted toward him, it seemed, surrounded him, and he . . . embraced . . . it.
Light filled him. Heat filled him. He could see the Stone, see the others watching him—Loial and Hurin anxiously, Selene showing no doubt that he could save her—but they might as well not have been there. The light was all. The heat and the light, suffusing his limbs like water sinking into dry sand, filling him. The symbol burned against his flesh.  All. The symbol . . .
Suddenly, as if the sun had gone out for the blink of an eye, the world flickered. And again. The symbol was a live coal under his hand; he drank in the light. The world flickered. Flickered. It made him sick, that light; it was water to a man dying of thirst. Flicker. He sucked at it. It made him want to vomit; he wanted it all. Flicker. The triangle-and-circle seared him; he could feel it charring his hand. Flicker. He wanted it all! He screamed, howling with pain, howling with wanting.

Hands pulled at him; he was only vaguely aware of them. He staggered back; the void was slipping away, the light, and the sickness that twisted at him. The light. He watched it go regretfully. Light, that’s crazy to want it.  I was so . . . Dazed, he stared at Selene. It was she who held his shoulders, stared wonderingly into his eyes.  The heron brand was there, but nothing else. No triangle-and-circle burned into his flesh.
“Remarkable,” Selene said slowly. She glanced at Loial and Hurin. The Ogier looked stunned, his eyes as big as plates; the sniffer was squatting with one hand on the ground, as if unsure he could support himself else. “All of us here, and all of our horses. And you do not even know what you did. Remarkable.”
“Are we . . . ?” Rand began hoarsely, and had to stop to swallow.
“Look around you,” Selene said. “You’ve brought us home.” She gave a sudden laugh. “You brought all of us home.”
For the first time Rand became aware of his surroundings again. The hollow surrounded them without any stairs, through here and there lay a suspiciously smooth piece of stone, colored red, or blue. The column lay against the mountainside, half buried in the loose rock of a fall. The symbols were unclear, here; wind and water had worked long on them. And everything looked real. The colors were solid, the granite a strong gray, the brush green and brown. After that other place, it seemed almost too vivid.
“Home,” Rand breathed, and then he was laughing, too. “We’re home.”  Hurin danced a caper.
 “I knew that you could.”
Rand’s laughter died. “I—I suppose I did.” He glanced at the fallen Portal Stone and managed a weak laugh. “I wish I knew what it was I did, though.”
Selene looked deep into his eyes. “Perhaps one day you will know,” she said softly. “You are surely destined for great things.”
Her eyes seemed as dark and deep as night, as soft as velvet. Her mouth . . . If I kissed her . . . He blinked and stepped back hurriedly, clearing his throat. “Selene, please don’t tell anyone about this. About the Portal Stone, and me. I don’t understand it, and neither will anybody else. You know how people are about things they don’t understand.”
Her face wore no expression at all. Suddenly he wished very much that Mat and Perrin were there. Perrin knew how to talk to girls, and Mat could lie with a straight face. He could manage neither very well.
Suddenly Selene smiled, and dropped a half-mocking curtsy. “I will keep your secret, my Lord Rand al’Thor.”
Rand glanced at her, and cleared his throat again. Is she angry with me? She’d certainly be angry if I had tried to kiss her. I think. He wished she would not look at him as she was, as if she knew what he was thinking. “Hurin, is there any chance the Darkfriends used this Stone before us?”
The sniffer shook his head ruefully. “They were angling to the west of here, Lord Rand. Unless these Portal Stone things are more common than I’ve seen, I’d say they’re still in that other world. But it wouldn’t take me an hour to check it. The land’s the same here as there. I could find the place here where I lost the trail there, if you see what I mean, and see if they’ve already gone by.”
Rand glanced at the sky. The sun—a wonderfully strong sun, not pale at all—sat low to the west, stretching their shadows out across the hollow. Another hour would bring full twilight. “In the morning,” he said. “But I fear we’ve lost them.” We can’t lose that dagger! We can’t! “Selene, if that’s the case, in the morning we will take you on to your home. Is it in the city of Cairhien itself, or . . . ?”
“You may not have lost the Horn of Valere yet,” Selene said slowly. “As you know, I do know a few things about those worlds.”
“Mirrors of the Wheel,” Loial said.
She gave him a look, then nodded. “Yes. Exactly. Those worlds truly are mirrors in a way, especially the ones where there are no people. Some of them reflect only great events in the true world, but some have a shadow of that reflection even before the event occurs. The passage of the Horn of Valere would certainly be a great event. Reflections of what will be are fainter than reflections of what is or what was, just as Hurin says the trail he followed was faint.”
Hurin blinked incredulously. “You mean to say, my Lady, I’ve been smelling where those Darkfriends are going to be? The Light help me, I wouldn’t like that. It’s bad enough smelling where violence has been, with out smelling where it will be, too. There can’t be many spots where there won’t be some kind of violence, some time. It would drive me crazy, like as not. That place we just left nearly did.  I could even smell it on us. On all of us. Even on you, my Lady, if you’ll forgive me for saying so. It was just that place, twisting me the way it twisted your eye.” He gave himself a shake. “I’m glad we’re out of there. I can’t get it out of my nostrils yet, all the way.”
Rand rubbed absently at the brand on his palm. “What do you think, Loial? Could we really be ahead of Fain’s Darkfriends?”
The Ogier shrugged, frowning. “I don’t know, Rand. I don’t know anything about any of this. I think we are back in our world. I think we are in Kinslayer’s Dagger. Beyond that . . . ” He shrugged again.
“We should be seeing you home, Selene,” Rand said. “Your people will be worried about you.”
“A few days will see if I’m right,” she said impatiently. “Hurin can find where he left the trail; he said so. We can watch over it. The Horn of Valere cannot be much longer reaching here. The Horn of Valere, Rand. Think of it. The man who sounds the Horn will live in legend forever.”
“I don’t want anything to do with legends,” he said sharply. But if the Darkfriends get by you . . . What if Ingtar lost them? Then the Darkfriends have the Horn of Valere forever, and Mat dies. “All right, a few days. At the worst, we will probably meet Ingtar and the others. I can’t imagine they’ve stopped or turned back just because we . . . went away.”
“A wise decision, Rand,” Selene said, “and well thought out.” She touched his arm and smiled, and he found himself again thinking of kissing her.
“Uh . . . we need to be closer to where they’ll come. If they do come. Hurin, can you find us a camp before dark, somewhere we can watch the place where you lost the trail?” He glanced at the Portal Stone and thought about sleeping near it, thought of the way the void had crept up on him in sleep the last time, and the light in the void. “Somewhere well away from here.”
“Leave it in my hands, Lord Rand.” The sniffer scrambled to his saddle. “I vow, I’ll never sleep again without first I see what kind of stone there is nearby.”
As Rand rode Red up out of the hollow, he found himself watching Selene more than he did Hurin. She seemed so cool and self-possessed, no older than he, yet queenly, but when she smiled at him, as she did just then . . . Egwene wouldn’t have said I was wise. Egwene would have called me a woolhead. Irritably, he heeled Red’s flanks.


this is going to have a link list of the main topics
This is the header!
And a subheader.
This is an example page.


Verstappen wins 2019 Brazil GP as Ferrari drivers collide
by Unnatee GidithuriAn unlikely top three saw Gasly finish 2nd for Toro Rosso, while Sainz was promoted to 3rd place after Hamilton’s penalty.
The 2019 Brazil GP served up quite the spectacle with strategy, battles and crashes all coming into play. After 71 laps, Max Verstappen was able to successfully convert his pole position into a race win. Pierre Gasly finished the race in 2nd place to secure his first-ever podium finish, while Carlos Sainz finished 3rd to complete the unexpected top three. Another major talking point of the race ended up being Ferrari’s double retirement as Sebastian Vettel and Charles Leclerc collided in the closing stages of the race.
The start saw Verstappen make a clean getaway, while Hamilton passed Vettel to move up to 2nd place. Behind them, Leclerc – who had to start from down in 14th place due to an engine penalty – kick-started a blistering attack through the grid, and was already up to 8th place by lap 5.
Hamilton was the first of the frontrunners to pit, attempting an undercut on lap 21, with Verstappen following suit one lap later. Despite the Red Bull crew pulling off a quick 1.9sec pit stop, Verstappen ended up losing time in the pit lane as Williams released Robert Kubica in the Red Bull driver’s path – an incident for which Kubica was hit with a 5sec time penalty. This dropped Verstappen to 2nd place, but he quickly got to work trying to catch up to Hamilton and on the next lap, he re-took the race lead into Turn 1.
Hamilton and Verstappen opted for soft tyres, thereby committing to a two-stop strategy; meanwhile, Vettel and Valtteri Bottas went for a different route by pitting for a pair of medium and hard tyres, respectively. However, neither drivers were able to pull off a single-stop strategy and had to pit again late. Bottas kicked-off the second round of pit stops on lap 41, followed by Hamilton on lap 44, Verstappen a lap later and Vettel on lap 50. After spending several laps trying to find a way past Leclerc, Bottas ultimately grinded to a halt, smoke billowing out the rear of his Mercedes. The incident triggered a safety car, bunching up the field.
While Verstappen pitted once again under the safety car, Hamilton slotted into the lead as Mercedes opted to keep him out. With Vettel having pitted just a few laps ago and Leclerc and Alexander Albon both pitting under the safety car, this meant Hamilton was the only one of the frontrunners on older tyres – the British driver even complained to his engineer that he was going to be a “sitting duck” at the restart. And Hamilton’s frustrations came true as Verstappen breezed past him into Turn 1. Meanwhile behind them, Albon passed Vettel to move up to 3rd place.
Verstappen spent the next few laps pulling away, but it was a tightly bunched-up field behind him. The race was then turned on its head on lap 66 as Ferrari’s race imploded. Leclerc passed Vettel for 4th place down the inside into Turn 1, but Vettel fought back on the run down to Turn 4, the duo making contact in the process. The incident left Leclerc with a broken front right wheel and Vettel with a right rear puncture, taking out both the drivers on the spot. With just a handful of laps to go, the safety car was brought out once again and Mercedes opted to pit Hamilton yet again, dropping him to 4th place. This left Verstappen in the lead, followed by Albon and Gasly.
By the time the safety car pulled in, there were just two laps left, making it a mad dash to the chequered flag. Hamilton immediately found a way past Gasly at the re-start, but his optimistic move on Albon for 2nd place resulted in him tagging the Red Bull, spinning Albon out. At the front, Verstappen safely crossed the finish line to secure his third win of 2019. Gasly took a spectacular 2nd-place finish – a redeeming moment after the French driver lost his Red Bull drive to Albon and was demoted to Toro Rosso earlier this year.
While Albon could only manage a 14th-place finish, Hamilton continued and crossed the line in 3rd place. However, after the race, the stewards determined that Hamilton was “predominantly at fault” for the collision and he was handed a 5sec penalty, dropping him to 7th place. This promoted Carlos Sainz to 3rd place, handing McLaren its first top-three finish since the 2014 Australian GP. A sensational drive saw the Spanish driver fight his way up the order after starting all the way down in 20th place.
The Alfa Romeo team also enjoyed a strong race, securing its best result of the year as Kimi Räikkönen and Antonio Giovinazzi finished 4th and 5th, respectively. They were followed by Daniel Ricciardo in 6th place ahead of Hamilton. Lando Norris, Sergio Pérez and Daniil Kvyat rounded up the top 10.
Results
| POS | DRIVER | CAR | LAPS | GAP |
|---|---|---|---|---|
| 1 | Max Verstappen | Red Bull/Honda | 71 | 1h33m14.678s |
| 2 | Pierre Gasly | Toro Rosso/Honda | 71 | 6.077s |
| 3 | Carlos Sainz Jr. | McLaren/Renault | 71 | 8.896s |
| 4 | Kimi Räikkönen | Alfa Romeo/Ferrari | 71 | 9.452s |
| 5 | Antonio Giovinazzi | Alfa Romeo/Ferrari | 71 | 10.201s |
| 6 | Daniel Ricciardo | Renault | 71 | 10.541s |
| 7 | Lewis Hamilton | Mercedes | 71 | 11.139s |
| 8 | Lando Norris | McLaren/Renault | 71 | 11.204s |
| 9 | Sergio Pérez | Racing Point/Mercedes | 71 | 11.529s |
| 10 | Daniil Kvyat | Toro Rosso/Honda | 71 | 11.931s |
| 11 | Kevin Magnussen | Haas/Ferrari | 71 | 12.732s |
| 12 | George Russell | Williams/Mercedes | 71 | 13.599s |
| 13 | Romain Grosjean | Haas/Ferrari | 71 | 14.247s |
| 14 | Alexander Albon | Red Bull/Honda | 71 | 14.927s |
| 15 | Nico Hülkenberg | Renault | 71 | 18.059s |
| 16 | Robert Kubica | Williams/Mercedes | 70 | 1 Lap |
| 17 | Sebastian Vettel | Ferrari | 65 | Collision |
| 18 | Charles Leclerc | Ferrari | 65 | Collision |
| 19 | Lance Stroll | Racing Point/Mercedes | 65 | Not running |
| - | Valtteri Bottas | Mercedes | 51 | Power Unit |


@include('./partials/_header.html')
@include('./partials/_first-screen.html')
@include('./partials/_about.html')
@include('./partials/_orange-block.html')
@include('./partials/_product.html')
@include('./partials/_why-rentberry.html')
@include('./partials/_team.html')
@include('./partials/_subscription.html')
@include('./partials/_footer.html')
@include('./partials/modals/stay-updated.html')
@include('./partials/modals/video.html')
@include('./partials/modals/email-verified.html')
@include('./partials/modals/roadshow.html')
@include('./partials/modals/modalGallery.html')


John graduated Norristown Area High School in 2016; only last year! Currently John is in his first year of college studying Political Science at Temple University. John also currently serves as a Democratic committee-person in West Norrition Township. John and his family are invested in Norristown, John received 13 years of a education from the Norristown Area School Distict, as did his sister. Their mother has worked in the district for 14 years. John has a close relationship to Norristown
John is only 19 years old, which is a big reason as to why he is running. John has realized that the only way for the Democratic party and progressive ideas to advance, is by embracing younger people into the party. John believes he can bring a new and fresh voice. John is not the voice of one, but many.
Having only graduated from Norristown Area High School last year, John has a perspective of the district that the Board has ever heard before. John understands how to improve student lives and take this district in a modern direction. Students, staff, and the faculty could appreciate a voice that understands the district from firsthand experiences.
I ask you to please support my campaign as we aim to get younger people involved in the political process and bring a fresh voice to the board!


Hey there , My name is Howard, designer, illustrator and digital artist.
I love connecting brands and companies with their customers through good design, I can help you with logo,
illustrations and digital design. I teach design on skillshare and I post very often on dribbble, but
enough about me, how can I help you?


{% extends 'cruditor/base.html' %} {% block content %}
This is some dummy content so show the minimal stuff needed to have a Cruditor supported views.


Solar Calculator [ver 4.0] |
| * Date:
of
|
| * Time:
:
hours
|
| * Time Zone:
:
of GMT
|
| * Latitude: ° North South |
| * Longitude: ° East West |
* Wall Azimuth angle: °
, With respect to
True North, or Magnetic North
(e.g. 0° for North facing wall and going clockwise-90° for East facing wall and so on) |
|
* Magnetic Declination: °
East West
|
| * Inclination of receiving plane to the horizon: °
(e.g. 90° for vertical wall, 0° for horizontal plane) |
| * Output angles with respect to: True North, or Magnetic North |
|
*
with data every
minutes
|
|
*
Output solutions in HTML too.
|
| *
|
| |
Source repository: https://github.com/asheshwor/solar-calculator
|


Check me out Sexually & professional
Resume
.
Contact me for sex
@username
or by
email
.