Embedding Media Masher in Your Site
The Media Masher editor consists of dozens of web components that can be embedded directly in HTML without requiring any intermediate build step. You can simply use modern import syntax to pull in the main component code, and then include the main MEDIA-MASHER element in your page.
The browser will automatically bind the code to the element, which will load sub components that contain the UI. Several attributes control the loading process, but their default values will display the editor with most of its features fully enabled.
Nonetheless, the JavaScript files are organized into two NPM style packages, if some sort of build step is required. The @mashermedia/client
package contains client specific code and @mashermedia/lib
has code shared with the server. These packages are NOT available in NPM’s public registry though, so you must point to the packed TGZ files for each library:
npm i https://js.masher.media/0.0.1-beta.36/client/package.tgz
npm i https://js.masher.media/0.0.1-beta.36/lib/package.tgz
You are also welcome to download the packages directly, and incorporate them into your project any way you see fit.
Component
The HTML document below can be hosted on any web host to display the Media Masher application in any modern web browser:
Example: Functional HTML
<html>
<head>
<title>Media Masher Functional Example</title>
<script type='importmap'>{ "imports": {
"lit": "https://cdn.jsdelivr.net/gh/lit/dist@3.2.0/all/lit-all.min.js",
"@mashermedia/": "https://js.masher.media/0.0.1-beta.36/"
} }</script>
<script type='module'>
import '@mashermedia/client/media-masher.js'
</script>
<style>
body {
height: 100vh; display: flex;
margin: 0; padding: 0;
}
</style>
</head>
<body>
<media-masher></media-masher>
</body>
</html>
This example provides minimal styling to just fill the viewport with the editor - see the Styling Guide for all the CSS options.
The first SCRIPT element is an import map which tells the browser where to find the Media Masher and Lit modules. The second SCRIPT element imports the MEDIA-MASHER custom element, which is then used in the BODY.
Without further configuration, the component will populate itself with many subcomponents and load their corresponding JavaScript files. Some components will not load though. In particular the buttons that would trigger API calls will not display and no items will appear in the media browser.
Other approaches to displaying these elements are detailed below, but one simple way is to set the apis
attribute on the MEDIA-MASHER element:
Example: MEDIA-MASHER with apis
<media-masher apis='./client/json/apis.json'></media-masher>
When a URL is provided for the apis
attribute, the JSON formatted file it points to is loaded. ApiOptions objects defined in it are used to configure particular API calls, and thus display the components that might trigger them. The URL can be relative, in which case it is resolved against @mashermedia. The files in the json directory of the @mashermedia/client library generally offer reasonable default data.
The @mashermedia/client/json/apis.json
file describes an API that provides some shared shapes and fonts. These will appear in the media browser. It does not provide other functionality though, so buttons like save and encode will still not appear. See the Deploying Guide for more information on configuring more advanced APIs.
Attributes
In a similar way, the icons and phrases displayed in buttons and links can be loaded from JSON formatted files. Together with apis
, the MediaMasherElement web component class supports three attributes that can be used to control the data loading process:
Attribute | Type | Default | Data Format |
---|---|---|---|
apis | string | "" | ApiOptions[] |
icons | string | ’./client/json/icons.json’ | StringRecord |
phrases | string | ’./client/json/phrases.json’ | StringRecord |
When populated, the MEDIA-MASHER element will trigger a GET request to the URL supplied, expecting a JSON object with the correct format in return.
The default for the
apis
property is an empty string. This is what keeps buttons and other elements associated with API calls to not be included in the UI.
The following HTML explicitly defines values for all the loading attributes by supplying relative URLs for each:
Example: MEDIA-MASHER with laoding attributes
<media-masher
apis='./client/json/apis.json'
icons='./client/json/icons.json'
phrases='./client/json/phrases.json'
></media-masher>
A drawback to this approach is that requests will always use the GET method, so no additional headers or body can be supplied. A more complex request can be constructed by directly setting options on the element (see below). It’s even possible to supply the data directly, bypassing the need for a request altogether.
Options
Under the hood, the attribute values above are used to prepopulate the MEDIA-MASHER element’s options
property, which follows the MediaMasherOptions interface.
This is a fairly complex data structure containing options for each of the
Plugins supported by the system.
For instance, the apis
attribute is passed to the SyncPlugin along with other options described by the SyncPluginOptions interface. The icons
and phrases
are passed to the ElementPlugin along with other options in the ElementPluginOptions interface.
When manually setting the options detailed below, it’s important to keep the following rules in mind:
- Populated default attributes must be unset on the element with an empty string.
- Plugin options must be set immediately after importing the client library.
Given the example HTML page above, the JavaScript in the sections below can just replace the content of the second SCRIPT element to accommodate the second rule. The following HTML snippet demonstrates unsetting the relevant attributes:
Example: MEDIA-MASHER with attribute overrides
<media-masher icons='' phrases=''></media-masher>
Phrases
The ElementPlugin is responsible for loading the text that appears in buttons, links, and other UI elements. As the UI is loading, calls are made to the MEDIA-MASHER element’s phrase
method which first loads the plugin and then calls its phrase
method. The argument to these methods is an ElementPhraseArgs object, which includes an id
property containing a unique string ID for each phrase.
This plugin has many methods which respond differently depending on its options, which follow the ElementPluginOptions interface. These options are found under the element
property of the MEDIA-MASHER element’s options.
One property of this interface is phrases
which will contain the value of the phrases
attribute copied from the MEDIA-MASHER element. When this string is not populated, the method will fallback to the phrase
property, which is an object following the ElementPhraseOptions interface:
ElementPhraseOptions | Type | Default |
---|---|---|
record | StringRecord | {} |
request | string | EndpointRequest | StringRecord | "" |
For each phrase ID used in the editor, the plugin first checks for a value in the record
. If not found, it falls back to the request
, which triggers the following behavior based on its type:
- When a string is supplied, it will be used to make a GET request and the response will be copied to the StringRecord for subsequent lookups.
- When an
EndpointRequest
object is supplied, it will be used to make a request and the response will be copied to the StringRecord for subsequent lookups. - When a StringRecord object is supplied, it will be used directly for lookups.
If a request is made, it is made just once. Then the StringRecord object is checked for the ID and if not found, the ID itself is returned.
Example: supplying raw data for phrase.record
import '@mashermedia/client/media-masher.js'
const [element] = document.getElementsByTagName('media-masher')
element.options.phrase.record = { 'play': 'Start' }
Example: supplying URL for phrase.request
import '@mashermedia/client/media-masher.js'
const [element] = document.getElementsByTagName('media-masher')
element.options.phrase.request = './client/json/phrases.json'
Example: supplying EndpointRequest for phrase.request
import '@mashermedia/client/media-masher.js'
const [element] = document.getElementsByTagName('media-masher')
element.options.phrase.request = {
endpoint: './client/json/phrases.json',
init: { method: 'GET' }
}
Icons
The ElementPlugin is also responsible for loading the icons that appear in buttons, links, and other UI elements. As the UI is loading, calls are made to the MEDIA-MASHER element’s icon
method which first loads the plugin and then calls its icon
method. The argument to these methods is an ElementIconArgs object, which includes an id
property containing a unique string ID for each icon.
This plugin has many methods which respond differently depending on its options, which follow the ElementPluginOptions interface. These options are found under the element
property of the MEDIA-MASHER element’s options.
One property of this interface is icons
which will contain the value of the icons
attribute copied from the MEDIA-MASHER element. When this string is not populated, the method will fallback to the icon
property, which is an object following the ElementIconOptions interface:
ElementIconOptions | Type | Default |
---|---|---|
prefix | string | "" |
record | StringRecord | {} |
request | string | EndpointRequest | StringRecord | "" |
For each icon ID used in the editor, the plugin first checks for a value in the record
. If not found, it falls back to the request
, which triggers the following behavior based on its type:
- When a string is supplied, it will be used to make a GET request and the response will be copied to the StringRecord for subsequent lookups.
- When an
EndpointRequest
object is supplied, it will be used to make a request and the response will be copied to the StringRecord for subsequent lookups. - When a StringRecord object is supplied, it will be used directly for lookups.
If a request is made, it is made just once. Then the StringRecord object is checked for the ID - the value can either be a URL or SVG string. If not found and the prefix
is nonempty, a URL is constructed by appending it with the ID and ‘svg’ extension. Ultimately an SVG element is returned, either by fetching the URL or parsing the string.
Example: supplying raw data for icon.record
import '@mashermedia/client/media-masher.js'
const [element] = document.getElementsByTagName('media-masher')
element.options.element.icon.record = { 'play': 'svg/start.svg' }
Example: supplying URL for icon.request
import '@mashermedia/client/media-masher.js'
const [element] = document.getElementsByTagName('media-masher')
element.options.element.icon.request = './client/json/icons.json'
Example: supplying EndpointRequest for icon.request
import '@mashermedia/client/media-masher.js'
const [element] = document.getElementsByTagName('media-masher')
element.options.element.icon.request = {
endpoint: './client/json/icons.json',
init: { method: 'GET' }
}
Media
On the client, the SyncPlugin is ultimately
responsible for delivering the mash that appears in the player and timeline, as well as populating the media browser. As these components are loaded they call the MEDIA-MASHER element’s mash
and find
methods. These calls are ultimately passed to the plugin which will attempt to pass them on to a configured API, while caching the results for subsequent calls.
There are several different mechanisms that can be used to configure APIs, each of them involving the ApisPlugin in some way. Its options follow the ApisPluginOptions interface and can be found under the apis
property of the MEDIA-MASHER element’s options.
Its apiOptions
property is an array of ApiOptions objects that it uses to create Api instances as needed.
Directly configuring an API
In cases where the client has all the information needed to configure an API, the most straightforward approach is to simply add it to the apiOptions
array:
Example: supplying raw client data for apis.apiOptions
import '@mashermedia/client/media-masher.js'
const [element] = document.getElementsByTagName('media-masher')
element.options.apis.apiOptions = [{
id: 'api-id',
userId: 'user-id',
endpoints: { find: 'find', mash: 'mash', add: 'add' }
}]
Each API must have an id
and userId
associated with it. The endpoints
object is used to determine which of the API methods are supported. The keys correspond to method names and the values are URLs used to build the actual method requests.
Buttons that required a particular method will only display if there is a key for their method. For instance, the save button will only display if the add
key is present as it is in this example.
The find
method is enabled as well, so results from calling that endpoint are used to populate the media browser. The mash
method is also enabled, so results from calling that endpoint are loaded into the player and timeline.
Relative URLs are resolved to the current page. The ApiOptions interface also supports an optional prefix
string property that will be prepended to relative URLs before resolution. There is also an optional request
object following the EndpointRequest interface, which can be used to further control the API requests being made. Its endpoint
property is also prepended to relative URLs before resolution.
If a value is defined as an empty string, that method is consider enabled within the UI, but no API requests are made. It’s expected that some other mechanism will handle the method.
Directly supplying media
In cases where the client already has media data it wants to load, an API can be configured to simply load it without making a request. The data that would have been returned from its mash
or find
API methods can instead be defined under the same key in its options:
Example: supplying raw media data in ApiOptions
import '@mashermedia/client/media-masher.js'
const [element] = document.getElementsByTagName('media-masher')
element.options.apis.apiOptions = [{
id: 'api-id',
userId: 'user-id',
mash: {
id: 'mash-id', label: 'Mash',
type: 'video', source: 'mash',
color: '#00FF00'
},
find: [{
id: 'media-id', label: 'Butcherman',
type: 'image', source: 'text',
resources: [{
id: 'resource-id',
type: 'raw', source: 'woff2',
decoded: { family: 'Butcherman' },
request: { endpoint: 'https://fonts.gstatic.com/s/butcherman/v24/2EbiL-thF0loflXUBOdb5zK5qtxtX88.woff2'
}
}]
}]
}]
In this example, we’re loading an empty mash having a green background color and a font from the Google Fonts API. Note that the mash and media require an id
be supplied.
Fetching API configuration
The options for the SyncPlugin can be configured to initialize the APIs the first time one of these methods is called. These are found under the sync
property of the MEDIA_MASHER element’s options and follow the SyncPluginOptions interface:
SyncPluginOptions | Type | Default |
---|---|---|
apis | string | undefined |
mediaId | string | undefined |
request | EndpointRequest | undefined |
When apis
is empty (which it is by default) the plugin falls back to request
, otherwise it builds a GET request from the URL provided. If neither is defined, the find
method simply returns an empty array and the mash
method returns a MashMedia instance with a single empty Track.
Example: supplying URL for sync.apis
import '@mashermedia/client/media-masher.js'
const [element] = document.getElementsByTagName('media-masher')
element.options.sync.apis = './client/json/apis.json'
Example: supplying EndpointRequest for sync.request
import '@mashermedia/client/media-masher.js'
const [element] = document.getElementsByTagName('media-masher')
element.options.sync.request = {
endpoint: './client/json/apis.json',
init: { method: 'GET' }
}
The request being made here is to the ApisPlugin rather than to an API itself. When the mash
or find
methods are first called, this request is passed to its fetch
method to initialize the APIs themselves. If multiple APIs are configured to support the find
method, a SELECT element will appear in the media browser so the user can choose between them.
Once the APIs are initialized, the corresponding method is called on the appropriate Api instance. If the mediaId
is defined, it’s added to the arguments supplied to the mash
method.
See the Deploying Guide for more information on configuring APIs.