This blog is a break from the format of the rest of the series to give a quick list of all the APIs available for customization.
I’m choosing to give this out in blog format to emphasise that this is the tentative API set. Please give feedback, so we can adjust as needed by V1 – at which point it will get more formal documentation.
EDIT. Now StoreFront 3.0 is out, there is some formal API documentation. See here. This blog may still be a useful introduction and overview, and the APIs it lists are still valid – but there are now some extra ones.
The primary mechanism for customization relies on CSS and JavaScript, and some understanding of these is required. I also use JQuery in examples – though this is not a requirement of using the APIs.
Note that these APIs augment (rather than replace) other functional customizations, such as possible server side on StoreFront. By their nature these are client side APIs and as such are best for controlling the UX. For security or deep functional changes (such as excluding apps or farms) consider the Server Side StoreFront APIs.
API Structure
There are two core JavaScript classes, and a set of public CSS classes that can be manipulated. It is also possible to delve deeper by editing the HTML or leveraging non-public CSS classes or JavaScript methods. These methods are not encouraged – they are likely to be brittle and break over upgrades. However, you may find gaps in the API and this may be the only way forward. If and when this happens, please let us know via the forums, and we will endeavour to update the APIs in future versions to avoid the need for others to step of the beaten path.
CSS Classes
We use what I call ‘functional’ CSS. That is we annotate parts of our UI with CSS classes not just to change their appearance, but also to control how the work. A simple example of this might be to show/hide an element on different screens in the UI. A slightly deeper example might be to bind actions to buttons based on their CSS classes.
CSS statements should be put in the file custom\style.css.
We annotate the document/body with CSS classes that indicate the current state of the UI.
The following are all extremely useful
.large / .small
This indicates if the UI is the ‘large’ desktop/tablet UI or the ‘small’ phone UI. Many elements change based on this. For example we might have a CSS rules
<span style="color: brown;">.small .toolbar</span> {<span style="color: red;">display</span>:<span style="color: blue;">none</span>;}
This says that the element marked with class ‘toolbar’ should be hidden in the phone UI.
.highdpi
.myapps-view / .store-view / .desktops-view (etc)
For example you can force the toolbar to appear in the myapps (favourites) view (by using)
<span style="color: brown;">.myapps-view .toolbar</span> { <span style="color: red;">display</span>:<span style="color: blue;">block</span>; }
Beyond these top level annotations, there are several more straightforward CSS classes you need to be aware of.
.logo-container
Marker for elements containing the ‘Receiver’ logo, or a company logo to replace it. These containers are DIVs and the logo is set by indicating a background image.
<span style="color: brown;">.logo-container</span> { <span style="color: red;">background-image</span>: <span style="color: blue;">url('WCOO_logo_white.png')</span>; <span style="color: red;">background-size</span>: <span style="color: blue;">100px 50px</span>; }
Note if you want to specify a High DPI variant you mist indicate the size the image should be squished to (typically half its pixel size)
<span style="color: brown;">.highdpi .logo-container</span> { <span style="color: red;">background-image</span>: <span style="color: blue;">url('WCOO_logo_white@2x.png')</span>; <span style="color: red;">background-size</span>: <span style="color: blue;">100px 50px</span>; }
.theme-header-bgcolor .theme-header-color .theme-highlight-color
.button .button.default
Custom Areas
We have defined three custom areas within the UI, each of which is indicated by an ID that can be referenced from CSS and JavaScript. These elements are initially empty, but are expected to be used by extensions that need to add UI. Edit, we’ve added a load more including on the Web Login page and app details page. See the API doc referenced above.
#customTop
#customBottom
#customScrollTop
If you want to add custom content that does not have a fixed location (e.g. a popup) then it could live an any of these areas, or just be added to the end of the page dynamically. Typically changing the content of any of these is done with JavaScript from within the custom\script.js file. For example (using JQuery)
$(<span style="color: brown;">"#customScrollTop"</span>).html(<span style="color: brown;">"<div class='wwco-hello'>Hello World</div>"</span>);
Note that the HTML added includes its own custom class. It is recommended that you prefix all new classes with a company name to avoid clashes with the (many) CSS classes defined and used by X1.
To go with this you might add some CSS (in custom\style.css)
<span style="color: brown;">.wwco-hello</span> { <span style="color: red;">font-size</span>: <span style="color: blue;">20px</span>; <span style="color: red;">color</span>: <span style="color: blue;">red</span>; }
Note. If you inspect the HTML you will find that these areas are inside containers called plugin[name]. These elements should not be used directly in customization code.
Hook APIs
These are APIs used to intercept actions performed by X1 in order to modify or delay behaviour. An example might be to exclude or restyle apps, change a sort order, add a warning message etc.
Hook APIs are used by (re)defining their function, for example
CTXS.Extensions.preInitialize = <span style="color: blue;">function</span>(callback) { alert(<span style="color: brown;">"just starting"</span>); callback(); };
Note that many of these extensions take a callback that should be called, if and when you want the default behaviour to take place, as in the example above. Note also the trailing semicolon, which catches out non-JavaScript programmers all the time. This is an assignment preInitialize=<thing> so needs a semicolon to terminate it.
In the following examples ‘app’, ‘store’ and ‘category’ are objects that have useful properties, such as name and subscription state. Some of these properties may change across releases so exercise some caution(!).
Hook APIs That Simply Inform
postConfigurationLoaded
Called once we are configured to show the UI, but before the UI is visible.
CTXS.Extensions.postAppListLoaded = <span style="color: blue;">function</span>(store) { <span style="color: green;">// do stuff</span> };
postSubscribe
Inform that a subscription has just taken place. (User clicked ‘Add’)
CTXS.Extensions.postSubscribe = <span style="color: blue;">function</span>(app) { … };
postInstall
Inform that a previously subscribed app was added to this machine.
CTXS.Extensions.postInstall = <span style="color: blue;">function</span>(app) { … };
postRemove
Inform that an app was removed (User clicked ‘Remove’)
CTXS.Extensions.postRemove = <span style="color: blue;">function</span>(app) { … };
postLaunch
CTXS.Extensions.postLaunch = <span style="color: blue;">function</span>(app) { … };
preRedraw postRedraw
Normally HTML5 is pretty good at sorting out the UI – but occasionally this isn’t the case. In particular if you have added/removed elements that change the size of the scrolling area, then it is likely that the UI will be incorrect. To help handle these cases there are two extension callbacks to warn of significant UI changes, and there is an extension API to force it to recalculate any dynamic UI areas (see below).
CTXS.Extensions.preRedraw = <span style="color: blue;">function</span>() { <span style="color: green;">// useful for logging</span> }; CTXS.Extensions.postRedraw = <span style="color: blue;">function</span>() { <span style="color: green;">// consider forcing a re-layout</span> };
Hook APIs That Allow Delays / Cancellations
doLaunch doSubscribe doRemove doInstall
On each of these the customization might show a dialog, perform some checks (etc) but ultimately should call ‘action’ if (and only if) they want the operation to proceed.
CTXS.Extensions.doLaunch = <span style="color: blue;">function</span>(app, action) { <span style="color: green;">// call 'action' function if/when action should proceed</span> action(); }; CTXS.Extensions.doSubscribe = <span style="color: blue;">function</span>(app, action) { <span style="color: green;">// call 'action' function if/when action should proceed</span> action(); }; CTXS.Extensions.doRemove = <span style="color: blue;">function</span>(app, action) { <span style="color: green;">// call 'action' function if/when action should proceed</span> action(); }; CTXS.Extensions.doInstall = <span style="color: blue;">function</span>(app, action) { <span style="color: green;">// call 'action' function if/when action should proceed</span> action(); };
preInitialize postInitialize beforeLogon beforeDisplayHomeScreen postAppListLoaded noteNativeClient
These functions inform of UX changes and allow custom UX (e.g. message boxes) before proceeding. The callback() function is called when X1 should continue.
CTXS.Extensions.preInitialize = <span style="color: blue;">function</span>(callback) { <span style="color: green;">// do stuff</span> callback(); }; CTXS.Extensions.postInitialize = <span style="color: blue;">function</span>(callback) { <span style="color: green;">// do stuff</span> callback(); }; CTXS.Extensions.beforeLogon = <span style="color: blue;">function</span>(callback) { <span style="color: green;">// do stuff</span> callback(); }; CTXS.Extensions.beforeDisplayHomeScreen = <span style="color: blue;">function</span>(callback) { callback(); }; CTXS.Extensions.postConfigurationLoaded = <span style="color: blue;">function</span>() { <span style="color: green;">// do stuff</span> };
Note Native Client is used when X1 is embedded in a native receiver.
It returns a Java Object describing the environment it is in (e.g. OS)
CTXS.Extensions.noteNativeClient = <span style="color: blue;">function</span>(properties) { <span style="color: green;">// do stuff</span> };
Deeper Changes – Controlling the App Lists
X1 has built in policy about which applications are excluded on a particular device (typically those that can’t run on it) and what is considered and ‘app’ or a ‘deskto’. However this can be overridden to force/prevent an app from appearing either in the UI as a whole, or specifically on a given screen. It is also possible to sort/filter the various lists of apps shown in different views/categories.
Note that the functions for ‘all apps’ and ‘desktops’ are called ‘filter’ rather than ‘sort’ because it is anticipated that we might add sort function later for different views of these collections. All these functions can be used to sort/filter the application list, and it is even add items to the list.
noteStartLoadApps
This is called at the beginning of processing apps for a given store. It is a good time to clear any cached lists.
CTXS.Extensions.noteStartLoadApps = <span style="color: blue;">function</span>(store) {};
noteApp
This is called for each app object that is to be included in the store (see below to adjust the filtering criteria). An extension might choose to modify an app here (for example to mark it as a desktop) or to remember the app object to add to a category or bundle later.
CTXS.Extensions.noteApp = <span style="color: blue;">function</span>(app) {};
excludeApp includeApp
Used to override the built in default for which apps to ignore on this device/browser. If both extensions return ‘false’ then the default policy is used.
CTXS.Extensions.excludeApp = <span style="color: blue;">function</span>(app) { <span style="color: green;">// return true or false</span> }; CTXS.Extensions.includeApp = <span style="color: blue;">function</span>(app) { <span style="color: green;">// return true or false</span> };
includeInMyApps excludeFromMyApps sortMyAppList*
Use to filter the ‘favourites’ view. Note it is possible to sort this, but as the user can drag-drop icons to rearrange, this should be used with caution (unless that feature is disabled).
CTXS.Extensions.includeInMyApps = <span style="color: blue;">function</span>(app) { <span style="color: green;">// return true or false</span> }; CTXS.Extensions.excludeFromMyApps = <span style="color: blue;">function</span>(app) { <span style="color: green;">// return true or false</span> }; CTXS.Extensions.sortMyAppList = <span style="color: blue;">function</span>(appArray,defaultSortFn) { <span style="color: green;">// Process appArray in place</span> };
sortCategoryAppList sortBundleAppList filterAllAppsDisplay filterDesktops
In place filter / sort of lists of apps/desktops. You can also re-add objects snared during the noteApp call – but also consider just changing the app properties during that call so it is naturally included where you want it.
CTXS.Extensions.sortCategoryAppList = <span style="color: blue;">function</span>(appArray,category, defaultSortFn) { <span style="color: green;">// Process appArray in place</span> }; CTXS.Extensions.sortBundleAppList = <span style="color: blue;">function</span>(appArray,bundle, defaultSortFn) { <span style="color: green;">// Process appArray in place</span> }; CTXS.Extensions.filterAllAppsDisplay = <span style="color: blue;">function</span>(appArray,defaultSortFn) { <span style="color: green;">// Process appArray in place</span> }; CTXS.Extensions.filterDesktops = <span style="color: blue;">function</span>(desktopArray,defaultSortFn) { <span style="color: green;">// Process appArray in place</span> };
preProcessAppData
This is a low level, and powerful call that gives the raw JSON data returned from the server in response to a query about apps. It can be used to modify the application list before any of the above processing takes place – however it is generally preferable to use the ‘safer’ methods above.
CTXS.Extensions.preProcessAppData = <span style="color: blue;">function</span>(store,jsondata) { <span style="color: green;">// Process jsonData in place</span> };
Other Deep Hooks
includeAuthenticationMethod excludeAuthenticationMethod
In a similar way to the app inclusion/exclusion, you can control the authentication menchanisms offered. This is only applicable to X1 in a web browser.
CTXS.Extensions.includeAuthenticationMethod = <span style="color: blue;">function</span>(authMethod) { <span style="color: green;">// return true or false</span> }; CTXS.Extensions.excludeAuthenticationMethod = <span style="color: blue;">function</span>(authMethod) { <span style="color: green;">// return true or false</span> };
Application specific styling
Each application object has a property called cssClass that is normally not set, but can be used by an extension to mark the HTML generated when this app is displayed. For example you might use one of the extensions above to set app.cssClass=“wwco-importantApp” and then add CSS such as
<span style="color: brown;">.wwco-importantApp .app-tile</span> { <span style="color: red;">background</span>:<span style="color: blue;">red</span>; <span style="color: red;">width</span>:<span style="color: blue;">200px</span>; <span style="color: red;">height</span>:<span style="color: blue;">100%</span>; <span style="color: red;">position</span>:<span style="color: blue;">absolute</span>; }
In general it is a bad idea to use this to hide apps (display:none) – as it will confuse the alignment logic. Better to exclude an app from the screen(s) where you don’t want it shown.
Action APIs
The ActionAPIs are calls to X1 to make it do something. Often these are made in the context of one of the hooks above (but not always).
CTXS.ExtensionAPI.appPlugin(plugin)
CTXS.ExtensionAPI.showMessage(data)
This shows a message box, styled to match X1 dialogs. ‘data’ is an object containing a number of optional fields.
CTXS.ExtensionAPI.showMessage({ localize: <span style="color: blue;">true</span>, <span style="color: green;">// if true try to localize messages</span> messageText: <span style="color: red;">"Hello"</span>, <span style="color: green;">// Text of message</span> messageTitle: <span style="color: red;">"Title"</span>, <span style="color: green;">// Title of message (optional)</span> okButtonText: <span style="color: red;">"go"</span>, <span style="color: green;">// Text for ok button (optional)</span> cancelButtonText: <span style="color: red;">"stop"</span>, <span style="color: green;">// Text for second button (optional)</span> isalert: <span style="color: blue;">true</span>, <span style="color: green;">// if true show an alert style message (optional)</span> okAction: <span style="color: blue;">function</span>() {}, <span style="color: green;">// action to do if ok is clicked (optional)</span> cancelAction: <span style="color: blue;">function</span>(){}, <span style="color: green;">// action to do if cancel is clicked</span> <span style="color: green;">// (optional)</span> });
Note that if cancelAction is null, then the cancel button is not shown.
CTXS.ExtensionAPI.resize()
CTXS.ExtensionsAPI.trace(message)
CTXS.ExtensionsAPI.refresh()
CTXS.ExtensionsAPI.changeView(view,customName)
CTXS.ExtensionsAPI.addToolbarButton(class,text,content,action)
Add a button to the toolbar. The toolbar is currently shown only on the ‘store’ view, though that may change, and it is possible to override this via css customization.
- Class is a class for the button itself. This should be provided to enable styling (for example to show/hide the button). Each button must have a unique class.
- Text is the text to show on the button face.
- Content is optional HTML content to display when this button is clicked. If provided, then the button is modal and will be highlighted when clicked (and other buttons unhighlighted). If omitted (null) then the button is not modal. Typically this HTML will itself include class references to enable styling.
- Action is an optional function to call when the button is clicked.
CTXS.ExtensionAPI.addToolbarButton(<span style="color: red;">"wwco-button"</span>,<span style="color: red;">"Test"</span>, <span style="color: red;">"<div class='wwco-page'>Do not click this button again</div>"</span>);
CTXS.ExtensionsAPI.addViewButton(class,text,name)
This adds a top level ‘view’ to the page, alongside ‘Favourites’, ‘Desktops’, ‘Apps’.
- Class is the class for both the button and the view. If the name is ‘foo’ then the button class will be ‘foo-view’ and this class will also be added to when the view is selected. This is then used to control the display by providing custom content triggered by this class.
- Text is text for the button face,
- Name is the name of the view, in case it is shown in the UI (as is the case with the phone UI today).
CTXS.ExtensionAPI.addViewButton(<span style="color: red;">"wwco-help"</span>,<span style="color: red;">"Help"</span>, <span style="color: red;">"WWCO Help"</span>);
Typically used with additional script such as:
$(<span style="color: red;">"#customScrollTop"</span>).html(<span style="color: red;">"Call 911"</span>);
And additional style such as:
<span style="color: brown;">.wwco-helpPage</span> { <span style="color: red;">display</span>:<span style="color: blue;">none</span>; } <span style="color: brown;">.wwco-help-view .wwco-helpPage</span> {<span style="color: red;"> display</span>:<span style="color: blue;">block</span>; }
CTXS.ExtensionsAPI.localStorageGetItem(key) CTXS.ExtensionsAPI.localStorageSetItem(key,value)
CTXS.ExtensionAPI.openURL(url)
“Command Line” Arguments
When the X1 UI is started you can specify a number of flags to it, either as part of the query or part string in the URL. (I.e. after a ? or a #). The most useful of these are the following:
- -ph Force the UI to show a phone UI, regardless of browser or screen dimensions.
- -lg Force the UI to show the larger UI, regardless of browser or screen dimensions
That’s all for this blog. Next time I’m going to take an aside to talk about NetScaler Gateway, then in subsequent blogs I’m going to dig into how to use the APIs above to achieve some useful effects.
Blogs in this series