Webviews¶
General¶
Support for window.print¶
New in version 3.7.0.
Apps support the window.print
JavaScript-API.
The standard system print dialog is opened if window.print
is called.
Support for window.open¶
Apps support the window.open
JavaScript-API. Starting with 3.11 links
opened via this API will be opened in the internal App Browser (with title bar, status bar and navigation, as a “new window”)
or the same webview, according to the standard browser specifications.
Starting with version 3.10 Action URLs can also be opened via this API on all platforms.
Cookies¶
Starting with version 3.13.0 cookies are enabled for all webviews in the Android and iOS apps.
Accessing dynamic resources¶
Starting with version 5.1 it is possible to access files inside the dynamic resources by using the resource
scheme.
These urls must have the following structure:
resource://dynamic/<path>
The path should point towards a file inside the dynamic resources. These paths are resolved based on device’s preferred languages. The same goes for all relative paths if the html itself was loaded using this scheme. This allows reducing the total size of the dynamic resources bundle by putting common files in the default folder and localization related files in their respective folders (e.g. de, en, …). For further information on dynamic resources and how files are resolved based on the device’s preferred languages see the corresponding page.
JavaScript-Interfaces¶
Using the Purple JavaScript-API¶
Web content can access special JavaScript-APIs to get information about the app / issue and trigger actions.
There are two ways to access the JavaScript-APIs: automatic and manual inclusion.
Automatic inclusion (deprecated since 3.9)¶
Changed in version 3.9.0: Deprecated, replaced with manual inclusion
With automatic inclusion you simply need to have a global onPurpleLoad
function which will get called after the APIs have been made available.
This however only works on Android and iOS and is not supported in the Web Kiosk. This method is therefore considered deprecated since Purple Release 3.9 with the support of manual inclusion.
Manual inclusion¶
New in version 3.9.0.
To manually include the JS-APIs html consumers can add any source ending on scripts/purpleInterface.js
or scripts/purpleInterface.min.js
to their head element.
The web view intercepts this request and returns the purpleInterface.js
.
It is recommended to use the following URL to assure the API works on all platforms
including web.
1 2 3 4 5 6 | <html>
<head>
<script type="text/javascript" src="https://kiosk.purplemanager.com/scripts/purpleInterface.min.js"></script>
</head>
<body></body>
</html>
|
Note
The old automatic injection mechanism is used as fallback strategy.
When manually embedding the purpleInterface.js source, the use of the onPurpleLoad
function is not mandatory.
The purple-Object is available instantly after loading the script.
Hint
If the onPurpleLoad function is used anyways, it needs to be defined prior to the above script tag.
<html>
<head>
<script type="text/javascript">
function onPurpleLoad() {
// the global "purple" object is now available
}
</script>
<script type="text/javascript" src="https://kiosk.purplemanager.com/scripts/purpleInterface.min.js"></script>
</head>
<body></body>
</html>
window.purple = {
app: {
...
},
metadata: {
...
},
storefront: {
...
},
store: {
...
},
issue: {
...
},
state: {
...
},
tracking: {
...
},
media: {
...
},
/**
* Close the current in app browser or article view
*/
closeView: function() {
// Implementation
}
}
Due to the asynchronous nature of the javascript bridge all api calls that return values require a callback function as a parameter. This function is then called by the native implementation with the value as its parameter. A common usage would look like this:
1 2 3 4 5 6 7 8 9 10 11 | function callbackFunctionForSomething(valueOfSomething) {
console.log(valueOfSomething);
}
window.purple.getSomething(callbackFunctionForSomething);
// or inline:
window.purple.getSomething(function(valueOfSomething) {
console.log(valueOfSomething)
});
|
closeView
The closeView
method can be used to close certain views such as the in app browser, entitlement and article views.
It is only possible to close modal views and not embedded ones.
App¶
This interface is for app-wide information such as the device’s connectivity state.
Note
This interface is not available in Web Kiosk.
window.purple = {
/**
* @public
* @static
* @namespace AppController
*/
app: {
/**
* Adds a listener for connection state changes.
* The listener will be called with a ConnectionState object.
* This listener will also be called with the current state right after
* calling this method.
*/
addConnectionStateListener: function (listener) {
// Implementation
},
/**
* Removes a listener for connection state changes.
*/
removeConnectionStateListener: function (listener) {
// Implementation
},
/**
* Adds a listener for lifecycle changes.
* The listener will be called with a LifecycleEvent object.
* This listener will also be called with the current state right after
* calling this method.
*/
addLifecycleListener: function (listener) {
// Implementation
},
/**
* Removes a listener for lifecycle changes.
*/
removeLifecycleListener: function (listener) {
// Implementation
},
/**
* Close the onboarding screen. If true is passed as the first parameter
* the onboarding will be shown again on the next app start.
*/
closeOnboarding: function (showAgain) {
// Implementation
}
}
}
addConnectionStateListener
The addConnectionStateListener
method can be used to register a callback function that gets called when the device changes its connection state.
The listener will also be called with the current state when this method is called.
This method takes a single parameter: A callback function that gets called with one parameter, a json object.
This json object will consist of a state
with the value ONLINE
and type
of either TYPE_3G
during mobile connectivity or TYPE_WLAN
when it is connected to wi-fi.
{
"state": "ONLINE",
"type": "TYPE_3G|TYPE_WLAN"
}
If the device is offline then there will be only a state
with the value OFFLINE
.
{
"state": "OFFLINE"
}
removeConnectionStateListener
This method removes the listener that was added with addConnectionStateListener
to stop receiving callbacks.
addLifecycleListener
The addLifecycleListener
method can be used to register a callback function that gets called when the webview or the device changes its lifecycle state.
The listener will also be called with the current state when this method is called.
This method takes a single parameter: A callback function that gets called with one parameter of type object.
This object will consist of a type
with the following values
STARTED
if the webview appearsRESUMED
if the webview is visible and gets focusPAUSED
if the webview is visible but loses focusSTOPPED
if the webview disappears
When the app comes to foreground or background and the webview is presented, the callback will also be called with the specific type
.
1 2 3 | {
"type": "STARTED|RESUMED|PAUSED|STOPPED"
}
|
removeLifecycleListener
This method removes the listener that was added with addLifecycleListener
to stop receiving callbacks.
closeOnboarding
Close the onboarding screen. If true is passed as the first parameter the onboarding will be shown again on the next app start.
This API method is only available on the onboarding screen.
See the onboarding documentation for more information about this feature.
App-Browser¶
This interface can be used to retrieve information about the configuration of the current webview, e.g. if it’s displayed modally or embedded, has titlebar and controls.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | window.purple = {
appBrowser: {
/**
* Get the display mode for the current webview.
*/
getDisplayMode: function (callback) {
// Implementation
},
/**
* Get the titlebar configuration for the current webview.
*/
isTitleBarEnabled: function (callback) {
// Implementation
},
/**
* Get the controls configuration for the current webview.
*/
isControlsEnabled: function (callback) {
// Implementation
},
/**
* Get the statusbar configuration for the current webview.
*/
isStatusBarEnabled: function (callback) {
// Implementation
}
}
}
|
getDisplayMode
Get the display mode for the current webview.
This method has one parameter, a callback function which will get the display mode value in as a single string parameter.
Values can be embedded
or modal
.
isTitleBarEnabled
Get the titlebar configuration for the current webview.
This method has one parameter, a callback function which will get a boolean value in as a single parameter.
isControlsEnabled
Get the controls configuration for the current webview.
This method has one parameter, a callback function which will get a boolean value in as a single parameter.
isStatusBarEnabled
Get the statusbar configuration for the current webview.
This method has one parameter, a callback function which will get a boolean value in as a single parameter.
Metadata¶
Metadata / information about the app and issue can be accessed through this javascript interface.
window.purple = {
/**
* @public
* @static
* @namespace MetaDataController
*/
metadata: {
/**
* Get metadata values by key. A callback function with a single parameter is required that will be called
* with the value for the given key.
*
* @param {string} key the metadata key
* @param {Function} callback the callback for the value
*/
getMetadata: function (key, callback) {
// Implementation
}
}
}
getMetadata
This method returns the value for a given key. The value will be provided via the callback method.
The following keys are available:
app_id
The id of the app in the Purple Manager.
On macOS this always returns “Preview Publication”.
Available contexts
- Entitlement HTML Login
- Dynamic HTML-Content
- In-App-Browser
- Storytelling Content
app_version
The version of the app.
Available contexts
- Entitlement HTML Login
- Dynamic HTML-Content
- In-App-Browser
- Storytelling Content
preview_app
Boolean value indicating if the app is a preview or release app.
On macOS this always returns true
.
Available contexts
- Entitlement HTML Login
- Dynamic HTML-Content
- In-App-Browser
- Storytelling Content
device_id
The unique id of the device. Used for communication with the Purple Manager.
Available contexts
- Entitlement HTML Login
- Dynamic HTML-Content
- In-App-Browser
- Storytelling Content
device_model
The model of the device.
Available contexts
- Entitlement HTML Login
- Dynamic HTML-Content
- In-App-Browser
- Storytelling Content
device_os
The os version of the device.
Available contexts
- Entitlement HTML Login
- Dynamic HTML-Content
- In-App-Browser
- Storytelling Content
platform
The platform (android
, kindle
, ios
, web` or ``macOS
) on which this app is running on.
Available contexts
- Entitlement HTML Login
- Dynamic HTML-Content
- In-App-Browser
- Storytelling Content
locale
The current locale of the system.
Available contexts
- Entitlement HTML Login
- Dynamic HTML-Content
- In-App-Browser
- Storytelling Content
manager_base_url
The base url for communicating with the delivery service of the Purple Manager.
Available contexts
- Entitlement HTML Login
- Dynamic HTML-Content
- In-App-Browser
- Storytelling Content
push_registration_token
The push registration token. This can be used to send pushes to the app.
Available contexts
- Entitlement HTML Login
- Dynamic HTML-Content
- In-App-Browser
- Storytelling Content
entitlement_login
The entitlement username of the user if he is logged in.
Available contexts
- Dynamic HTML-Content
- In-App-Browser
- Storytelling Content
entitlement_token
The entitlement access token of the user if he is logged in.
Available contexts
- Entitlement HTML Login
- Dynamic HTML-Content
- In-App-Browser
- Storytelling Content
entitlement_forced_login_enabled
The configuration parameter for forced entitlement login on app start.
Available contexts
- Entitlement HTML Login
- Dynamic HTML-Content
- In-App-Browser
- Storytelling Content
entitlement_login_mode
Can be either login
or relogin
indicating the current mode / reason why the login screen has been opened. relogin
will be returned
if the server responded with an error that the access token is invalid during subscription validation.
Available contexts
- Html-Entitlement Login
entitlement_mode
Can be either entitlement
, oauth
or none
indicating the mode of the first entitlement server that is configured for the app.
Available contexts
- Entitlement HTML Login
- Dynamic HTML-Content
- In-App-Browser
- Storytelling Content
entitlement_refresh_token
The refresh token that is being used to request a new access token for the entitlement api calls. This value is only available if oauth is being used as entitlement server.
Available contexts
- Entitlement HTML Login
- Dynamic HTML-Content
- In-App-Browser
- Storytelling Content
issue_id
The id of the currently viewed issue.
Available contexts
- Storytelling Content
- Dynamic HTML-Content (when opened via an ST-Action)
- In-App-Browser (when opened via an ST-Action)
issue_name
The name of the currently viewed issue.
In the Composer Preview on macOS this always returns “Preview Publication”.
Available contexts
- Storytelling Content
- Dynamic HTML-Content (when opened via an ST-Action)
- In-App-Browser (when opened via an ST-Action)
issue_alias
The alias of the currently viewed issue.
Available contexts
- Storytelling Content
- Dynamic HTML-Content (when opened via an ST-Action)
- In-App-Browser (when opened via an ST-Action)
publication_id
The id of the publication of the currently viewed issue.
In the Composer Preview on macOS this always returns “Preview Publication”.
Available contexts
- Storytelling Content
- Dynamic HTML-Content (when opened via an ST-Action)
- In-App-Browser (when opened via an ST-Action)
publication_name
The name of the publication of the currently viewed issue.
On macOS this always returns “Preview Publication”.
Available contexts
- Storytelling Content
- Dynamic HTML-Content (when opened via an ST-Action)
- In-App-Browser (when opened via an ST-Action)
page_id
The id of the currently viewed content page.
Available contexts
- Storytelling Content
page_alias
The alias of the currently viewed content page.
Available contexts
- Storytelling Content
page_title
The title of the currently viewed content page.
Available contexts
- Storytelling Content
page_index
The index of the currently viewed content page.
Available contexts
- Storytelling Content
page_filename
The filename of the current page (not the webview page, but the storytelling content page)
Available contexts
- Storytelling Content
onboarding_mode
The mode for the onboarding screen. Can be appstart
when opened
during app start or manual
when opened via action url.
Available contexts
- HTML onboarding screen
Storefront¶
Web content can access storefront data through a javascript interface.
Note
This interface is not available in Composer Native Preview.
window.purple = {
/**
* @public
* @static
* @namespace StorefrontController
*/
storefront: {
/**
* Get subscriptions.
*
* @param {Function} callback the callback for the subscriptions
*/
getSubscriptions: function (callback) {
// Implementation
},
/**
* Gets a list of all publications. Callback will be called with a
* JSONArray of Publication objects.
*/
getPublications: function (callback) {
// Implementation
},
/**
* Gets a list of all issues for the publication with the given
* publicationId. Callback will be called with a JSONArray of Issue
* objects.
*/
getIssues: function (publicationId, callback) {
// Implementation
},
/**
* Gets a list of all issue states for the given issueIds. Callback
* will be called with a JSONArray of IssueState objects without a
* progress value.
*/
getIssueStates: function (issueIds, callback) {
// Implementation
},
/**
* Gets the issue object for the given issueId. The callback function
* will be called with the corresponding Issue object. If the issueId
* is the id of a preview issue, then the corresponding parent issue
* will be returned.
*/
getIssueById: function (issueId, callback) {
// Implementation
},
/**
* Starts the download of the issue with the given issueId.
* This can also be a preview issue.
*/
startDownload: function (issueId) {
// Implementation
},
/**
* Pauses the download of the issue with the given issueId.
* This can also be a preview issue.
*/
pauseDownload: function (issueId) {
// Implementation
},
/**
* Deletes the content of the issue with the given issueId.
* This includes the preview content and temporary downloaded data.
* The callback will be called with the current IssueState object.
*/
deleteIssue: function (issueId, callback) {
// Implementation
},
/**
* Adds a listener for issue state changes.
* The listener will be called with an IssueState object with a
* progress value.
*/
addIssueStateListener: function (listener) {
// Implementation
},
/**
* Removes a listener for issue state changes.
*/
removeIssueStateListener: function (listener) {
// Implementation
},
/**
* Loads the new storefront.
* The callback will be called with the StorefrontUpdateResult object.
*/
updateStorefront: function (callback) {
// Implementation
},
/**
* Adds a listener for newsstand and newsfeed changes.
*/
addUpdateListener: function (listener) {
// Implementation
},
/**
* Removes a listener for newsstand and newsfeed changes.
*/
removeUpdateListener: function (listener) {
// Implementation
}
/**
* Returns the base url for the files of the given issueId.
*/
getIssueBaseUrl: function (issueId, callback) {
// Implementation
},
/**
* Returns the pages information for the given issueId.
*/
getIssuePages: function (issueId, callback) {
// Implementation
},
/**
* Returns the toc information for the given issueId.
*/
getIssueToc: function (issueId, callback) {
// Implementation
},
/**
* Open a list of articles in a pager.
*/
openArticles: function (articleIds, initialArticleId, callback) {
// Implementation
},
/**
* Get a list of all categories. The callback will be called with a
* JSONArray of Category objects.
*/
getCategories: function (callback) {
// Implementation
}
}
}
getSubscriptions
Subscriptions can be accessed through the getSubscriptions
method.
It takes one parameter, a callback function, which is called with an array of
subscriptions.
{
"name": "One Month Subscription",
"productId": "com.sprylab.onemonth",
"duration": "one_month",
"hidden": false,
"unlocksAllContentDuringPeriod": true,
"index": 1,
"publicationIds": ["aabbcc", "1233456"],
"formattedPrice": "13.37€",
"price": 13.37,
"currency": "EUR",
"properties": [
{
"name": "testproperty",
"value": "testvalue"
}
],
"state": "NONE|PURCHASING|VALIDATING|PURCHASED"
}
getPublications
This method lists all publications in the storefront. It takes one parameter, a callback function, which is called with an array of publications.
{
"publicationId": "aabbcc",
"displayName": "Publication A",
"displayDescription": "Fancy publication description",
"properties": [
{
"name": "testproperty",
"value": "testvalue"
}
],
"index": 0,
"type": "KIOSK|CHANNEL",
"thumbnails": {
"default": "url",
"kind1": "url"
}
}
getIssues
This method needs to be called to obtain a list of issues for a specific publication.
It takes two parameters: A publicationId
and a callback function which then will be called with an array of issues.
The publicationId
can be acquired from a publication model that is obtained through the getPublications method.
{
"issueId": "aabbcc",
"publicationId": "ddeeff",
"alias": "alias2",
"displayName": "Issue A",
"displayDescription": "Fancy issue description",
"properties": [
{
"name": "testproperty",
"value": "testvalue"
}
],
"index": 0,
"pubDate": 123,
"contentLength": 1337,
"numberOfPages": 42,
"comingSoon": true,
"previewIssue": {
"id": "ddeeff",
"contentLength": 1337,
"numberOfPages": 42
},
"productId": "com.sprylab.issue1",
"thumbnails": {
"default": "url",
"kind1": "url"
},
"tags": ["tag1", "tag2", "tag3"],
"categories": ["categoryId1", "categoryId2"]
}
The pubDate
is a UNIX timestamp in ms.
If the issue has a preview issue then previewIssue
is a jsonObject with the preview issue’s id, its content size and the number of pages otherwise it is null
.
The productId
can be used to purchase the issue through the store
api. It can be null
if the issue is not purchasable through in app payments.
getIssueStates
To obtain the state of specific issues this method can be used. It takes two parameters. The first is an array of issue ids and the second a callback function which returns an array of issue state objects for the requested issue ids.
{
"issueId": "aabbcc",
"state": "<STATE>",
"updateAvailable": <true|false>
}
Certain states are only available depending whether progressive loading is enabled or not. Note that issues that are hidden through entitlement are not returned by getIssues anymore until they are unlocked. The following table describes the possible issue states.
State Description PL PK LOCKED locked through entitlement -> not visible for the user off <4.0 COMING_SOON coming soon enabled -> visible, but not downloadable any >=3.4.0 PURCHASABLE not purchased any >=3.4.0 AVAILABLE download possible -> purchased, or no paid content any >=3.4.0 INDEXING minimal required on >=4.0 PARTIALLY_INSTALLED_DOWNLOADING ready to be displayed, still loading and not finished yet on >=4.0 PARTIALLY_INSTALLED_PAUSED ready to be displayed, not loading and not finished yet on >=4.0 DOWNLOAD_PAUSED download started and paused off >=3.4.0 DOWNLOADING downloading (and extracting on >= PK 5.0) off >=3.4.0 INSTALLING extracting, post processing off <5.0 INSTALLED downloaded, extracted, available for reading any >=3.4.0 UPDATE downloaded and new version available (=local version does not match remote version) off <4.0
getIssueById
This method can be used to request information for a single issue with a given issueId. The callback function will be called with the corresponding Issue Object. See getIssues for the structure of such an object. When the passed issueId is the id of a preview issue then the corresponding parent issue is returned instead.
startDownload
With this method it is possible to start the download of an issue with the given issueId
.
pauseDownload
With this method it is possible to pause the download of the issue with the given issueId
.
deleteIssue
This method allows the deletion of an issue. It takes two parameters. The first is the issueId
of the issue that will be removed and the other is a callback function that will be called with an issue state object after the delete process completed.
addIssueStateListener
The addIssueStateListener
method allows the registration of a listener that will be called each time when the state of an issue changes.
This method takes a single parameter which is a callback function. It will be called with issue state objects which contain an additional progress
value.
This progress
can be a value between 0 and 100.
For a description of the states see the table in getIssueStates
.
{
"issueId": "aabbcc",
"state": "<STATE>",
"updateAvailable": <true|false>,
"progress": 0
}
removeIssueStateListener
This method removes a listener that was set with addIssueStateListener
to stop receiving issue state updates.
updateStorefront
This method starts a synchronization of the storefront with the Purple Manager. The result of this process will then be called on the given callback function.
For a successful synchronization it will be a simple json object:
{
"success": true
}
For failures it will be a json object which may contain an error code:
{
"success": false,
"error_code": "[OFFLINE|UNKNOWN]"
}
addUpdateListener
The addUpdateListener
method allows the registration of a listener that will be called each time the kiosk has been changed due to a sync or login change.
This method takes a single parameter which is a callback function which itself takes one parameter.
This parameter will be a JSON object with the following format:
1 2 3 4 | {
type: "PARTIAL|FULL|LOGIN|LOGOUT",
success: true
}
|
Or for errors:
1 2 3 4 5 | {
type: "PARTIAL|FULL|LOGIN|LOGOUT",
success: false,
error_code: "OFFLINE|UNKNOWN"
}
|
removeUpdateListener
This method removes a listener that was previously added with addUpdateListener
to stop receiving kiosk updates.
getIssueBaseUrl
This method can be used to retrieve the base url for the given issueId. If the issue is not downloaded yet, the callback is called with null
.
With this base url it is possible to request files of an issue. Requests to the pages.xml
and TOC.xml
will return 404 Not Found
.
To request the contents of these files, the storefront.getIssuePages()
and storefront.getIssueToc()
methods should be used.
getIssuePages
This method works the same as issue.getPages() except that it takes an issueId as parameter and returns the data for that issue. See issue.getPages() for details regarding the page model.
getIssueToc
This method works the same as issue.getToc() except that it takes an issueId as parameter and returns the data for that issue. See issue.getToc() for details regarding the toc model.
openArticles
Opens a list of articles by their issue ID in a native pager. Invalid issues, e.g. paid issues, non-channel issues, will be filtered. If no issues remain to be opened, the callback will be called with the error_code NO_ISSUES_FOUND.
The second parameter may be an issue ID from the list of issue IDs which will be the initially opened issue. If the issue ID is invalid, the first valid issue will be shown.
When the articles have been successfully opened the callback function will be called with a simple json object:
1 2 3 | {
"success": true
}
|
For failures it will be a json object which may contain an error code:
1 2 3 4 | {
"success": false,
"error_code": "[NO_ISSUES_FOUND|UNKNOWN]"
}
|
getCategories
Gets a list of all categories. The callback will be called with a JSONArray like the following example:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | [
{
"id": "categoryId1",
"name": "Category name",
"thumbnailURL": "http://",
"properties": { "name": "value" },
"categories": [
{
"id": "categoryId2",
"name": "Category name 2",
"thumbnailURL": "http://",
"properties": { "name": "value" },
"categories": []
}
]
},
{
"id": "categoryId3",
"name": "Category name",
"thumbnailURL": "http://",
"properties": { "name": "value" },
"categories": []
}
]
|
Store¶
It is also possible to start purchases and manage the subscription codes through a javascript interface.
Note
This interface is not available in Web Kiosk and Composer Native Preview.
window.purple = {
/**
* @public
* @static
* @namespace StoreController
*/
store: {
/**
* Purchase a product
*
* @param {string} productId
* @param {Function} callback
*/
purchase: function (productId, callback) {
// Implementation
},
/**
* Subscribe to a subscription
*
* @param {string} productId
* @param {Function} callback
*/
subscribe: function (productId, callback) {
// Implementation
},
/**
* Restores purchases on device. Only available on iOS
* (Android will call callback immediately).
*
* @param {Function} callback
*/
restorePurchases: function (callback) {
// Implementation
},
/**
* Set a function as a listener which should be called when
* the purchase state of a subscription product did change.
* @param {Function} listener
*/
setPurchaseStateListener: function (listener) {
// Implementation
},
/**
* Get the current subscription codes
*
* @param {Function} callback
*/
getSubscriptionCodes: function (callback) {
// Implementation
},
/**
* Add subscription codes
*
* @param {String[]} codes
* @param {Function} callback
*/
addSubscriptionCodes: function (codes, callback) {
// Implementation
},
/**
* Remove the subscription codes
*
* @param {String[]} codes
* @param {Function} callback
*/
removeSubscriptionCodes: function (codes, callback) {
// Implementation
},
/**
* Gets the price information for the given productIds.
* The callback will be called with a JSONArray of ProductInfo objects.
*/
getPrices: function (productIds, callback) {
// Implementation
}
}
}
purchase
The purchase
method can be used to purchase a single product, e.g. an
issue.
It takes two parameters: the product id and a callback function.
The callback function gets called with one parameter.
For successful purchases it will be a simple json object:
{
"success": true
}
For failures it will be a json object which may contain an error code:
{
"success": false,
"error_code": "CANCELLED"
}
subscribe
The subscribe
method can be used to purchase a subscription.
It takes two parameters: the product id and a callback function.
The callback function gets called with one parameter.
For successful purchases it will be a simple json object:
{
"success": true
}
For failures it will be a json object which may contain an error code:
{
"success": false,
"error_code": "CANCELLED"
}
restorePurchases
Previously purchased products and subscriptions can be restored using the
restorePurchases
method.
It takes one parameter, a callback function, which gets called when the restore has finished.
The callback function gets called with one parameter: a success or failure result object. See purchase / subscribe for details about this object.
Note
This call is only available on iOS. It will do nothing on Android and call the callback function immediately with a success-object.
setPurchaseStateListener
Set a function as a listener which will be called when the purchase state of a subscription product changed.
Note
This call is only available on iOS. It will do nothing on Android.
getSubscriptionCodes
Get the current subscription codes.
It takes one parameter, a callback function, which gets called with the subscription codes in a string array as the only parameter.
addSubscriptionCodes
Add and activate (multiple) subscription codes.
It takes two parameters: the codes as a string array and a callback function, which gets called with a success or failure result object. See purchase / subscribe for details about this object.
removeSubscriptionCodes
Remove and deactivate (multiple) subscription codes.
It takes two parameters: the codes as a string array and a callback function, which gets called with a success or failure result object. See purchase / subscribe for details about this object.
getPrices
This method requests the price information for given product ids. It takes two parameters: The first is an array of product ids and the second a callback method that will be called with an array of product info objects.
{
"productId": "some.product.id",
"formattedPrice": "13.37€",
"price": 13.37,
"currency": "EUR"
}
Issue¶
This API can be used to retrieve information (e.g. pages and toc from the pages.xml
and TOC.xml
)
of the current issue.
window.purple = {
issue: {
getPages: function (callback) {
// Implementation
},
getToc: function (callback) {
// Implementation
}
}
}
getPages
The getPages
method can be used to retrieve all pages.
It takes one parameter: a callback function.
The callback function gets called with one parameter: an array of page model objects.
{
"id": "",
"pageIndex": 1,
"pageNumber": 1,
"pageLabel": "Seite 1",
"title": "Seite 1",
"shortTitle": "Seite 1",
"alias": "Seite 1",
"showPurchaseSuggestion": true,
"placeholder": false,
"excludeFromPaging": true,
"targetURL": "page-id.stxml",
"thumbnailURL": "thumbs/thumb-page123.jpg",
"sharingEnabled": true,
"sharingText": "Text",
"sharingURL": "http://example.com",
"customData": "tag1,tag2"
}
getToc
The getToc
method can be used to retrieve all toc pages.
It takes one parameter: a callback function.
The callback function gets called with one parameter: an array of toc page model objects.
{
"pageId": "<page-id>",
"pageAlias": "Page Alias",
"title": "Title 123",
"section": "Section",
"shortTitle": "Old Content Short-Title",
"teaser": "Old Content Only",
"thumbnailURL": "thumbs/thumb-page123.jpg"
}
State¶
This API can be used to store custom state for usage in different webviews. It can only store string values.
window.purple = {
state: {
setState: function (key, value) {
// Implementation
},
getState: function (key, callback) {
// Implementation
}
}
}
Note
On macOS the state is stored on a per document bases inside the users defaults. This means that the state data is not part of the Purple-Project files.
setState
The setState
method can be used to store a string value for a string key.
It takes two parameters: the key and a value.
If the value is null
the key gets deleted.
Hint
The key is case-sensitive.
getState
The getState
method can be used to retrieve a string value for a string key.
It takes two parameters: the key and a callback function.
The callback function gets called with two parameters: the key and the value.
If there is no value for the given key, the value will be null
.
Hint
The key is case-sensitive.
Tracking¶
This API can be used to track custom events in web views (e.g. purchase over an external entitlement or custom view like read mode). There are three different types of events:
- Actions
- Views
- Purchases
The events are forwarded to the app’s enabled tracking services. The same configuration mechanism like in the apps is used.
Note
User Attributes are currently not supported.
Note
All the keys in the optionalParams object will be automatically converted to upper case.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 | window.purple = {
tracking: {
/**
* Track an action, e.g. a tap on a button.
*
* @param {string} key the event name
* @param {Object} [optionalParams] can be sent with each event if the tracking service supports this.
* Every key will be included in the event. The values can contain all placeholders supported for the event
* and will be evaluated (see app tracking) when sending the event to the service.
* @param {Function} [callback] is called when the event has been tracked
*/
trackAction: function (key, optionalParams, callback) {
},
/**
* Track a view, e.g. a currently shown screen.
*
* @param {string} key the key of the screen event
* @param {Object} [optionalParams] can be sent with each event if the tracking service supports this.
* Every key will be included in the event. The values can contain all placeholders supported for the event
* and will be evaluated (see app tracking) when sending the event to the service.
* @param {Function} [callback] is called when the event has been tracked
*/
trackView: function (key, optionalParams, callback) {
},
/**
* Track a purchase.
*
* @param {string} key the purchase event key
* @param {string} productId
* @param {number} price
* @param {string} currencyCode
* @param {string} transactionId
* @param {Object} [optionalParams] can be sent with each event if the tracking service supports this.
* Every key will be included in the event. The values can contain all placeholders supported for the event
* and will be evaluated (see app tracking) when sending the event to the service.
* @param {Function} [callback] is called when the event has been tracked
*/
trackPurchase: function (key, productId, price, currencyCode, transactionId, optionalParams, callback) {
}
}
}
|
Note
The optionalParams
(key-value pairs) can be sent with each event if the tracking service supports this. Every key will be included in the event. The values can contain all placeholders supported for the event and will be evaluated (see app tracking) when sending the event to the service.
trackAction
The trackAction
method can be used to track a custom action event.
It takes two parameters: key and optionalParams. Starting with 3.14.0 the method takes a callback function which is called when the event has been tracked.
trackView
The trackView
method can be used to track a custom view event.
It takes two parameters: key and optionalParams. Starting with 3.14.0 the method takes a callback function which is called when the event has been tracked.
trackPurchase
The trackPurchase
method can be used to track a custom purchase event.
It takes six parameters: key, productId, price, currencyCode, transactionId and optionalParams. Starting with 3.14.0 the method takes a callback function which is called when the event has been tracked.
Media¶
This API can be used to play remote audio streams and files. The audio will continue playing even if the user puts the app to the background.
background_mode_audio_enabled
boolean
false
This property enables the background audio mode for iOS apps. This allows the playback to continue when the app is moved to the background.
Note that enabling this property also means that other audio applications get interrupted.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 | window.purple = {
media: {
startAudio: function (displayName, url) {
// Implementation
},
pauseAudio: function () {
// Implementation
},
resumeAudio: function () {
// Implementation
},
stopAudio: function () {
// Implementation
},
seekTo: function(time) {
// Implementation
},
setPlaybackRate: function (rate) {
// Implementation
},
getPlaybackRate: function (callback) {
// Implementation
},
addStatusListener: function (statusListener) {
// Implementation
},
removeStatusListener: function (statusListener) {
// Implementation
},
addProgressListener: function (progressListener) {
// Implementation
},
removeProgressListener: function (progressListener) {
// Implementation
}
}
};
|
startAudio
Starts a remote audio stream or file. This method takes two parameters: a display name used for notification and player UI and the URL to play.
pauseAudio
Pauses the current audio playback.
resumeAudio
Resumes the current audio playback.
stopAudio
Stops the current audio playback.
seekTo
Seeks to the given time in the current audio playback. This method takes the desired time in milliseconds.
setPlaybackRate
Sets the playback rate to the specified value. Only values between 0.5 and 2.0 are allowed. Values outside these bounds will be clipped to their respective limits. The playback rate gets reset to the default rate of 1.0 on app restarts.
getPlaybackRate
Requests the current playback rate which will be the single parameter of the callback function.
addStatusListener
Adds a listener for playback state changes.
removeStatusListener
Removes a listener for playback state changes.
addProgressListener
Adds a listener for playback progress changes.
removeProgressListener
Removes listener for playback progress changes.
Web Player specifics¶
Due to WebViews being implemented using iframes in Web Player and Web Newsstand, JavaScript injection works different in this context.
The global purple object can be made available by including purpleInterface.js
in the embedded page. An interface for the Purple Object is
re-implemented in the child page. When a function of the purple object is
called, a HTML5 PostMessage will be sent to the parent window (Web Player / Web Newsstand).
This will invoke the actual call on the Purple Object. The Web Player / Web Newsstand will then
respond with another HTML 5 PostMessage which the child window (iframe) will
process.
From V 3.0.0 purpleInterface.js
is included in the Web Player repository.
The latest version is delivered via Purple DS | Web Newsstand.
It is recommended to include the script from one of the following URLs to assure to always use the latest version:
https://kiosk.purplemanager.com/scripts/purpleInterface.js
https://kiosk.purplemanager.com/scripts/purpleInterface.min.js
Note
Please be aware that only sites can be displayed which have the X-FRAME-OPTIONS
header set correctly.
Read here for details: https://developer.mozilla.org/en/docs/Web/HTTP/Headers/X-Frame-Options
purpleInterface.js (excerpt)
window.purpleInterface = {
callbacks: {},
util: {
receiveMessage: function (event) {
try {
// get response data
var responseData = JSON.parse(event.data);
var value = responseData.value;
var callbackId = responseData.callbackId;
var key = responseData.key;
if (callbackId) {
// call callback function from callback map
if(key) {
window.purpleInterface.callbacks[callbackId](key, value);
} else {
window.purpleInterface.callbacks[callbackId](value);
}
// delete callback
window.purpleInterface.callbacks[callbackId] = null;
} else if(key === 'RELOAD'){
window.document.location.reload();
} else if(key === 'HISTORY_BACK') {
window.history.back();
} else if(key === 'HISTORY_FORWARD') {
window.history.forward();
} else if(key === 'DOCUMENT_TITLE') {
window.purpleInterface.util.postMessage('DOCUMENT_TITLE', 'DOCUMENT_TITLE', document.title);
}
} catch (e) {
}
},
postMessage: function (type, key, value, callback) {
if (window !== window.parent) {
// create requestData
var requestData = {
type: type,
key: key
};
if (value) {
requestData.value = value;
}
if (callback) {
// create id = index in callback array
var callbackId = window.purpleInterface.util.generateUUID();
requestData.callbackId = callbackId;
// add callback to callback array
window.purpleInterface.callbacks[callbackId] = callback;
}
// call postMessage
window.parent.postMessage(JSON.stringify(requestData), '*');
}
},
generateUUID: function () {
var d = new Date().getTime();
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
var r = (d + Math.random() * 16) % 16 | 0;
d = Math.floor(d / 16);
return (c === 'x' ? r : (r & 0x3 | 0x8)).toString(16);
});
}
}
};
document.addEventListener('DOMContentLoaded', function () {
window.addEventListener('message', window.purpleInterface.util.receiveMessage);
window.purpleInterface.util.postMessage('LOAD', 'LOAD', null, function () {
if (!window.purple) {
window.purple = {};
var links = document.querySelectorAll('a[href^="purple://"], a[ ^="pkapp://"], a[href^="pkitem://"]');
for (var i = 0; i < links.length; i++) {
links[i].addEventListener('click', function (e) {
window.purpleInterface.util.postMessage('ACTION_URL', 1, this.href);
e.preventDefault();
});
}
// purple object
window.purple.metadata = {
getMetadata: function (key, callback) {
window.purpleInterface.util.postMessage('META', key, null, callback);
}
};
window.purple.state = {
setState: function (key, value) {
window.purpleInterface.util.postMessage('STATE', key, value, null);
},
getState: function (key, callback) {
window.purpleInterface.util.postMessage('STATE', key, null, callback);
}
};
window.purple.issue = {
getPages: function (callback) {
window.purpleInterface.util.postMessage('PAGES', 'PAGES', null, callback);
},
getToc: function (callback) {
window.purpleInterface.util.postMessage('TOC', 'TOC', null, callback);
}
};
window.purple.closeView = function() {
window.purpleInterface.util.postMessage('CLOSE_VIEW', 'CLOSE_VIEW');
};
if ('onPurpleLoad' in window && typeof onPurpleLoad === 'function') {
onPurpleLoad();
}
var search = window.location.search;
if (document.referrer){
if (search) {
search += '&' + document.referrer.split('?')[1];
} else {
search = '?' + document.referrer.split('?')[1];
}
}
history.replaceState({}, document.title, window.location.origin + window.location.pathname + search);
}
});
});