JavaScript API functions

Switch to

JavaScript API version 7

Changelog

In this release, we added upload(), displayModal() and displayToast().

Usage

The methods listed below are all available on the window.ShowpadLib object after page load. Simply call the desired method with the correct parameters.

When window.ShowpadLib is created and ready to go, the method window.onShowpadLibLoaded() will be called (if it exists).

Have a look at the examples to get some more context.

Methods

All methods returning a boolean will return true when the call was executed successfully and return false when something went wrong. The method returns false when a required parameter was not set or when the method was called again before the previous method was completed. Some methods return full objects or single values like getUserInfo(), upload() or getVersion()

getVersion

Returns the version of the integration. If this function does not exist, we can assume that version 2 is available.


let version = null;
if (typeof window.ShowpadLib.getVersion === 'function') {
    version = window.ShowpadLib.getVersion(); // Would be 6 in this case.
}
else {
    version = 2;
}

getUserInfo

Returns the information of the active user.

const userInfo = window.ShowpadLib.getUserInfo();

// userInfo will look like this:
const userInfo = {
    email: 'user@company.com',
    id: '123',
    user_name: 'user@company.com',
    full_name: 'Firstname Lastname'
};

getDeviceInfo

Returns the information of the current device. The locale value is a 2-letter code following the ISO 639-1 standard.

The app is one of the following values: web, ios, android, windows. It represents the app where this HTML Content is currently being viewed.

The version is a version code for the app where this HTML content is being viewed.

const deviceInfo = window.ShowpadLib.getDeviceInfo();

// deviceInfo will look like this:
const deviceInfo = {
    locale: 'en', // language of the device, following ISO 639-1
    app: 'web', // the app,
    version: '2' // the app version
};

hasFeature

Use this method to know if a specific feature is supported on the app your content is running in.

This method will (synchronously) return:

The list of features can be found in the table below.

Key Description
shareEmail Share content by sending an email. Useful in combination with the share() method
shareLink Share content by generating a link. Useful in combination with the share() method
collections Add content to a collection. Useful in combination with the addAssetsToCollection() method
if (window.ShowpadLib.hasFeature('shareLink')) {
    // The feature "shareLink" is supported on this platform.
}

trackEvent

Track an event in your Experience App. This is used to track how conversations are happening within the experience. The events can be exported through Showpad’s Export API. The events can be exported through Showpad’s Export API.

The method accepts a single event or an array of events. The order of the array is the order they are tracked in. In principle there are no constraints on the event format, but we recommend to adhere to the formats as shown in the example below, for compatibility with potential future reporting:


// PAGEVIEW
const pageview = {
  "type": "pageview",
  "touchpoint": {
    "id": "eventID",
    "name": "Event name",
    "sourceId": "pageID",
    "sourceName": "Page name",
    "destinationId": "pageID",
    "destinationName": "Page name",
    "fields": {
      "pageviewDepth": "category A/item B/detail C", // Path representing the depth of the page (in this example, you could compare on category level, item level and detail level)
      "pageviewType": "tab" // How the page is displayed (page, tab, slide, ...)
    }
  }
};

// EVENT
const event = {
  "type": "event",
  "touchpoint": {
    "id": "eventID",
    "name": "Event name",
    "sourceId": "pageID",
    "sourceName": "Page name",
    "fields": {
      "eventCategory": "Video", // The object that was interacted with
      "eventAction": "play", // The type of interaction
      "eventLabel": "Fall Campaign", // Useful for categorizing events
      "eventValue": 42 // A numeric value associated with the event
    }
  }
};

ShowpadLib.trackEvent(event); // Track a single event
ShowpadLib.trackEvent([event, pageview]); // Track multiple events

share

Shows a modal view to share contents. The provided assets will be added so they can be shared.

The type of the modal is either email or link.

If some of the assets are not shareable (expired, deleted, unshareable, …), the modal will still be shown, but the callback function will be called with result partial. In this case, the Showpad client will show a warning message to the user that some content cannot be shared.

If none of the provided assets can be shared, a warning will be shown, but no sharing modal will be displayed and the result will be error

const assetSlugs = ['asset1-slug','asset2-slug'];
const type = 'email'; // Can be 'email' or 'link'.
if (window.ShowpadLib.share(type, assetSlugs, callbackFn)) {
    // Call went through, callbackFn will be called.
}
else {
    // Something went wrong or the given type is not supported on this app.
}

function callbackFn (result) {
    if (result == 'success') {
        // all assets were added to the share dialog and the dialog is shown
    }
    else if (result == 'partial') {
        // some assets were not added to the share dialog, because they could be expired, unshareable, deleted, ...
        // the share-dialog is shown
    }
    else if (result == 'error') {
        // no assets were added and the dialog is NOT shown
    }
    else {
      // null was returned, something went wrong
    }
}

getAssetPreviewUrl

Returns the url with which the asset preview image can be loaded. The assetId is always required. The assetSlug is also required on mobile.

var assetId = 'asset1-id';
var assetSlug = 'asset1-slug';
var size = 400;
var imageEl = document.getElementById('image');
imageEl.src = ShowpadLib.getAssetPreviewUrl(assetId, assetSlug, size);

// The asset preview image should now be loaded in the image element.

Sizes

From and including: iOS 6.5, Windows v1.6.12
For all Android versions and before iOS 6.5, Windows v1.6.12

getAssetsByTags

Invokes the callback function with a list of assets that have all the given tags. This function works offline.

var tags = ['Tag 1','screen-15','nl'];
if (ShowpadLib.getAssetsByTags(tags, callbackFn)) {
    // Call went through, callbackFn will be called
}
else {
    // Something went wrong
}

function callbackFn (assets) {
    if (assets) {
        // Assets might look like this:
        const assets = [
                {
                    "id": "abc123", // Local id to the device
                    "slug": "bdfklje739v930g0d",
                    "name": "my_brochure.pdf",
                    "displayName": "My Brochure fo 2017.pdf",
                    "type": "document",
                    "description": "A description", // Can be null
                    "permissions": {
                        "share": true, // File can be shared
                        "annotate": true, // File can be annotated
                        "download": true // File can be downloaded
                    },
                    "tags": ["Tag 1","screen-15","nl"],
                    "previewUrl" : "https://endpoint/asset/abc123/preview", // Same as response from getAssetPreviewUrl
                },
                ...
            ];
    }
    else {
      // null was returned, something went wrong
    }
}

getAssetsInFolder

Invokes the callback function with a list of assets that can be found in the given folder. This function works offline.

Note: this will not work recursively and will only return actual assets. So it doesn’t return values from deeper levels nor will it return folders.

var folder = 'xyz2250'; // last part from showpad://folder/xyz2250, this can be the id or slug.
if (ShowpadLib.getAssetsInFolder(folder, callbackFn)) {
    // Call went through, callbackFn will be called
}
else {
    // Something went wrong
}

function callbackFn (assets) {
    if (assets) {
        // Assets might look like this:
        const assets = [
                {
                    "id": "abc123", // Local id to the device
                    "slug": "bdfklje739v930g0d",
                    "name": "my_brochure.pdf",
                    "displayName": "My Brochure fo 2017.pdf",
                    "type": "document",
                    "description": "A description", // Can be null
                    "permissions": {
                        "share": true, // File can be shared
                        "annotate": true, // File can be annotated
                        "download": true // File can be downloaded
                    },
                    "tags": ["Tag 1","screen-13"],
                    "previewUrl" : "https://endpoint/asset/abc123/preview", // Same as response from getAssetPreviewUrl
                },
                ...
            ];
    }
    else {
      // null was returned, something went wrong
    }
}

getAssetsByQuery

Invokes the callback function with a list of assets that match with the given query. This function works offline and thus cannot search within documents.

var query = 'onepager';
if (ShowpadLib.getAssetsByQuery(query, callbackFn)) {
    // Call went through, callbackFn will be called
}
else {
    // Something went wrong
}

function callbackFn (assets) {
    if (assets) {
        // Assets might look like this:
        const assets = [
                {
                    "id": "abc123", // Local id to the device
                    "slug": "bdfklje739v930g0d",
                    "name": "featured_product_onepager.pdf",
                    "displayName": "My Brochure fo 2017.pdf",
                    "type": "document",
                    "description": "A description", // Can be null
                    "permissions": {
                        "share": true, // File can be shared
                        "annotate": true, // File can be annotated
                        "download": true // File can be downloaded
                    },
                    "tags": ["Tag 1","screen-15","nl"],
                    "previewUrl" : "https://endpoint/asset/abc123/preview", // Same as response from getAssetPreviewUrl
                },
                ...
            ];
    }
    else {
      // null was returned, something went wrong
    }
}

getAssetFileUrl

Returns the url with which the asset file (the video, image, pdf,…) can be fetched. The assetId is always required. The assetSlug is also required on mobile.

var assetId = 'asset1-id';
var assetSlug = 'asset1-slug';
var videoEl = document.getElementById('video');
videoEl.src = ShowpadLib.getAssetFileUrl(assetId, assetSlug);

// The asset data should now be loaded in the video element.

getCollections

Invokes the callback function with a list of collection data ([{ id, name }]) or null when something went wrong.

if (window.ShowpadLib.getCollections(callbackFn)) {
    // Call went through, callbackFn will be called.
}
else {
    // Something went wrong
}

function callbackFn (collections) {
    if (collections) {
        // Collections might look like this:
        const collections = [
                {
                    id: '1',
                    name: 'Collection Name 1',
                },
                {
                    id: '2',
                    name: 'Collection Name 2'
                }
            ];
    }
    else {
      // null was returned, something went wrong
    }
}

openCollection

Shows a modal view with the contents of the collection. This view allows for the collection to be shared

const collectionId = '...' ;
if (window.ShowpadLib.openCollection(collectionId)) {
    // Modal will be opened
}
else {
    // Something went wrong
}

createCollection

Creates a new collection with the specified name, will invoke the callback function with the id of the new collection or null when an error occured

const name = 'My Collection Name';
if (window.ShowpadLib.createCollection(name, callbackFn)) {
    // Call went through, callbackFn will be called.
}
else {
    // Something went wrong
}

function callbackFn (collectionId) {
    if (collectionId) {
        // Do something with collection id
    }
    else {
       // Something went wrong
    }
}

addAssetsToCollection

Shows the modal which prompts the user the select the collection to add the assets to.
Will call the callback method with the id of the collection the assets were added to or null if the flow was canceled or an error occured

const assets = [];
assets.push('asset1-slug');
assets.push('asset2-slug');
if (window.ShowpadLib.addAssetsToCollection(assets, callbackFn)) {
    // Call went through, modal will be shown.
    // callbackFn will be called
}
else {
    // Something went wrong
}

function callbackFn (collectionId) {
    if (collectionId) {
        // Assets were added to the collection with provided collectionId
    }
    else {
       // Something went wrong or the user canceled the flow or
       // none of the given asset-slugs were found.
    }
}

addAssetsToCollectionWithId

Will add the given assets to the collection with the given id. If the collection does not exist, nothing will happen.

const collectionId = '...';
const assets = ['asset1-slug', 'asset2-slug'];
if (window.ShowpadLib.addAssetsToCollectionWithId(collectionId, assets, callbackFn)) {
    // Call went through, callbackFn will be called
}
else {
    // Something went wrong
}

function callbackFn (collectionId) {
    if (collectionId) {
        // Assets were added to the collection with provided collectionId
    }
    else {
       // Something went wrong or the user canceled the flow
    }
}

clearCollection

Removes all the items from the given collection

const collectionId = '...';
if (window.ShowpadLib.clearCollection(collectionId)) {
    // Call went through, items will be cleared
}
else {
    // Something went wrong
}

getShowpadApi

Invokes the callback function with an object with the required information to do an HTTP request to the Showpad API.

If the Showpad API returns a 401, it means your access token has expired, and you’ll need to request a new one. Do this using the refreshShowpadApi() method.

if (window.ShowpadLib.getShowpadApi(callbackFn)) {
    // Call went through, callbackFn will be called
}
else {
    // Something went wrong
}

function callbackFn (apiConfig) {
    if (apiConfig) {
        // apiConfig can look like this:
        const apiConfig = {
            accessToken: 'xyz',
            url: 'https://subdomain.showpad.biz', // no trailing slash
            error: null // or a string with the actual error
        };

        if (apiConfig.accessToken && apiConfig.url) {
            // Do an api-call
        }
        else if (apiConfig.error) {
            if (apiConfig.error === 'unavailable') {
                // no api key available
            }
            else {
                // something else went wrong
            }
        }
        else {
            // Something went wrong
        }
    }
    else {
       // Something went wrong or the user canceled the flow
    }
}

refreshShowpadApi

This method will refresh the Showpad access token and call the callback function with the same object as the getShowpadApi call

function doApiCall (url, config) {
    fetch(url, config)
            .then(response => {
                if (response.status < 200 || response.status >= 400) {
                    const error = new Error();
                    error.statusCode = response.status;
                    throw error;
                }
            })
            .catch(onApiCallFailed);
}
function onApiCallFailed (error) {
    if (error.statusCode === 401) {
        // Unauthorized, let's refresh the token
        refreshToken(url, config);
    }
}

function refreshToken (url, config) {
    if (window.ShowpadLib.refreshShowpadApi(apiConfig => {})) {
        // Call went through, callbackFn will be called
    }
    else {
        // Something went wrong
    }

    function callbackFn (apiConfig) {
        if (apiConfig) {
            // apiConfig can look like this:
            const apiConfig = {
                accessToken: 'xyz',
                url: 'https://subdomain.showpad.biz', // no trailing slash
                error: null // or a string with the actual error
            };

            if (apiConfig.accessToken && apiConfig.url) {
                // Refresh succeeded, retry the failed call.
                config.headers['Authorization'] = `Bearer ${apiConfig.accessToken}`;
                doApiCall(url, config);
            }
            else if (apiConfig.error) {
                if (apiConfig.error === 'unavailable') {
                    // no api key available
                }
                else if (apiConfig.error === 'expired') {
                    // Refresh Token has expired, no further calls can be made.
                }
                else {
                    // Something went wrong
                }
            }
            else {
                // Something went wrong
            }
        }
        else {
           // Something went wrong
        }
    }
}

getSalesforceApi

Invokes the callback function with an object with the required information to do an HTTP request to the Salesforce API.

If the Salesforce API returns a 401, it means your access token has expired, and you’ll need to request a new one. Do this using the refreshSalesforceApi() method.

if (window.ShowpadLib.getSalesforceApi(callbackFn)) {
    // Call went through, callbackFn will be called
}
else {
    // Something went wrong
}

function callbackFn (apiConfig) {
    if (apiConfig) {
        // apiConfig can look like this:
        const apiConfig = {
            accessToken: 'xyz',
            url: 'https://login.salesforce.com', // no trailing slash
            error: null // or a string with the actual error
        };

        if (apiConfig.accessToken && apiConfig.url) {
            // Do an api-call to Salesforce
        }
        else if (apiConfig.error) {
            if (apiConfig.error === 'unavailable') {
                // no Salesforce access token available
                // Possibly, the user is not connected to Salesforce.
            }
            else {
                // something else went wrong
            }
        }
        else {
            // Something went wrong
        }
    }
    else {
       // Something went wrong or the user canceled the flow
    }
}

Particularities with the Salesforce API and HTML Content

To call an api on a different domain (eg. “https://login.salesforce.com”), that service needs to have CORS enabled. More info about CORS or (Cross-Origin Resource Sharing) can be found here .

Salesforce has a specific settings-page to enable specific domains for your Salesforce instance. Salesforce requires this to be an “https://”-url. The problem is that your HTML Content is also rendered in our mobile apps, and they don’t use an “https://”-scheme, but something specific for the platform (eg. “ms-appx-package://” on Windows).

So calling the Salesforce API directly from your HTML Content, will probably result in a CORS error by Salesforce.

To circumvent this, we suggest to proxy all api-calls to Salesforce through an iframe. This iframe would link to a webpage hosted on a CORS-enabled domain. The javascript in the HTML Content would just call some javascript in that iframe, which would send it through to Salesforce and send the response back. The communication between the HTML Content and the iframe needs to happen with “postMessage” https://developer.mozilla.org/en-US/docs/Web/API/Window/postMessage.

The diagram below might make things more clear.

refreshSalesforceApi

This method will refresh the Salesforce access token and call the callback function with the same object as the getSalesforceApi call.

function doSalesforceApiCall (url, config) {
    fetch(url, config)
            .then(response => {
                if (response.status >= 200 && response.status < 300) {
                    return response;
                }
                const error = new Error();
                error.statusCode = response.status;
                throw error;
            })
            .catch(onSalesforceApiCallFailed);
}
function onSalesforceApiCallFailed (error) {
    if (error.statusCode === 401) {
        // Unauthorized, let's refresh the token
        refreshSalesforceToken(url, config);
    }
}

function refreshSalesforceToken (url, config) {
    if (window.ShowpadLib.refreshSalesforceApi(apiConfig => {})) {
        // Call went through, callbackFn will be called
    }
    else {
        // Something went wrong
    }

    function callbackFn (apiConfig) {
        if (apiConfig) {
            // apiConfig can look like this:
            const apiConfig = {
                accessToken: 'xyz',
                url: 'https://login.salesforce.com', // no trailing slash
                error: null // or a string with the actual error
            };

            if (apiConfig.accessToken && apiConfig.url) {
                // Refresh succeeded, retry the failed call.
                config.headers['Authorization'] = `Bearer ${apiConfig.accessToken}`;
                doApiCall(url, config);
            }
            else if (apiConfig.error) {
                if (apiConfig.error === 'unavailable') {
                    // no Salesforce access token available
                }
                else if (apiConfig.error === 'expired') {
                    // Salesforce Refresh Token has expired, no further calls can be made.
                }
                else {
                    // Something went wrong
                }
            }
            else {
                // Something went wrong
            }
        }
        else {
           // Something went wrong
        }
    }
}

upload

Uploads a single file to “My files”. Returns an EventEmitter which emits the upload status.

A file can be a File object or an ArrayBuffer object.


const filename = 'filename.txt';
const file = new File(['file contents'], filename);

const upload = {
    file: file,
    filename: filename
};

const statusEmitter = window.ShowpadLib.upload(upload);

The EventEmitter will emit seperate events for each specific type of progress the upload goes through.

Events

queued

Emitted when the host received the upload but has not yet started the upload.

Fires once

statusEmitter.on('queued', () => {
    // Do something when the upload is queued
});
uploading

Emitted when the upload has progressed and reports on the amount of bytes sent.

Can fire multiple times

statusEmitter.on('uploading', (data) => {
    // Do something when uploading
});

with the data object containing

data = {
  bytesTotal: 1000, // The total number of bytes to upload
  bytesSent: 500 // The number of bytes uploaded
}
processing

The upload is succesfully transferred to the server and the file is now being processed.

Fires once

statusEmitter.on('processing', () => {
    // Do something when processing
});
success

The upload has been successfully uploaded and is processed.

Fires once

statusEmitter.on('success', (data) => {
    // Do something when the upload succeeded
});

With the object containing

data = { 
    asset: {
        "id": "abc123",
        "slug": "bdfklje739v930g0d",
        "name": "my_brochure.pdf",
        "displayName": "My Brochure fo 2017.pdf",
        "type": "document",
        "description": "A description",
        "permissions": {
            "share": true,
            "annotate": true,
            "download": true
        },
        "tags": ["Tag 1","screen-15","nl"],
        "previewUrl" : "https://endpoint/asset/abc123/preview"
    }
}
failed

At some point the upload failed.

Fires once

statusEmitter.on('failed', (error) => {
    // Do something when upload failed
});

with the error object containing

error = {
  reason: "A wrong filetype was submitted" 
}

displayModal

displays a native modal with a title, text and clickable buttons.

title to specify a title for the modal

text the text contents displayed in the modal

buttons an array of buttons to display

A button must have 2 fields named reason and text and possibly one optional field called tinted

⚠ On Android only the first 3 buttons will be shown.

reason is a text field that is passed back in the callback whenever a buttons is clicked.

text is the text to display on the button

tinted is an optional parameter. When set to true the button has a distinctive seperate color

window.ShowpadLib.displayModal({
    title: "The modal title",
    text: "The modal text",
    buttons: [
        {reason: 'cancel', text: 'Cancel'},
        {reason: 'ok', text: 'OK', tinted: true}
        ...
    ]
}, (reason) =>  {
    // Modal closed with reason
});

displayToast

Display a native toast

text is the text to be displayed in the toast

actionText the text to be used on the action button

type must be one of info|success|error.

Depending on the type the toast color will be blue|green|red.

window.ShowpadLib.displayToast({
    type: "info",
    text: "The text displayed in the toast",
    actionText: "click me",
}, (reason) => {
    // Toast has been closed with reason
});

reason can be timeout or clicked depending if the user clicked before a timeout occured.