npm package: @specless/format-api
Description: Format API used in conjuction with CSF.624
Latest version: 1.0.4

# Format API
1. This is published as a npm package (update index.js), run `npm run share` to publish a new version to NPM.
2. This is a public npm package under the Specless NPM Org. It can be accessed via: https://unpkg.com/@specless/format-api@latest

# Structure
1. Make sure the API (`src/formatApi.js`) is available in your index.js.
2. Define a format and initialize the API.
3. Define one or more Slots.
4. For each slot, define one or more Canvases that will be loaded within each slot.

# Example Format
The below example uses the api to create a format that contains a 4:1 canvas that loads inside the ad server slot and an autoexpanding lightbox that loads on top of the page.

#### index.js
```
// Make sure formatAPI.js has been pasted or imported the file above.


// 1. Define the format.
var FORMAT = new SpeclessFormat('lightbox', format);



// 2. Define the slots.
var BANNER = new FORMAT.Slot('banner');

var LIGHTBOX = new FORMAT.Slot('lightbox', {
	target: document.getElementById('out-of-page-element'),
	state: 'hiding',
	role: 'overlay'
});



// 3. Define the Canvases that go in the slots.
var BANNER_CANVAS = new BANNER.Canvas('primary-canvas', {
	show: false
});

var LIGHTBOX_CANVAS = new LIGHTBOX.Canvas('overlay-canvas', {
	show: false
});



// 4. Define the Size and CSS for the Banner Slot
BANNER.size('4:1');

BANNER.containerCss({
	opacity: 0,
	transition: '1s ease opacity'
});

BANNER.containerCss({
	opacity: 1,
}, 'loaded'); // Will fade in one the slot's state is set to 'loaded'.



// 5. Define the Size and CSS for the Lightbox Slot
LIGHTBOX.size('800x600');

LIGHTBOX.wrapperCss({
	position: 'fixed',
	top: '50%',
	left: '50%',
	transform: 'translate(-50%, -50%)'
});

LIGHTBOX.wrapperCss({
	display: 'none'
}, 'hiding'); // hide if state is 'hiding'

LIGHTBOX.wrapperCss({
	display: 'block'
}, 'showing'); // show if state is 'showing'


// 6. Handle the Expand Action
FORMAT.handleAction('expand', function(event, resolve, reject) {
	LIGHTBOX_CANVAS.panel().show().then(function() {
		LIGHTBOX.state('showing');
		resolve();
	});
});

// 7. Handle the Close action
FORMAT.handleAction('close', function(event, resolve, reject) {
	LIGHTBOX_CANVAS.panel().hide().then(function() {
		LIGHTBOX.state('hidden');
		resolve();
	});
});

// 8. Load and show the Banner canvas, apply the loaded state, then check frequency data and auto-expand the creative if the user has not seen the format in the past day.

BANNER_CANVAS.panel().show().then(function() {
	BANNER.state('loaded')

	FORMAT.getFrequencyData().then(function(frequency) {
		var frequencyCount = frequency.day().format() // Number of times this format has been seen in past day
		if (frequencyCount == 0) {
			FORMAT.triggerAction('expand');
		}
	});
});

```


# Defining the Format and Initializing the API

## `SpeclessFormat(name, format)`
Constructs and returns the Format API.
`name` should be any string acceptable to use as an `id` (e.g. camel or snake case).

```
var FORMAT = new SpeclessFormat('my-awesome-format', format); // Pass in specless format object as second argument
```

# FORMAT API

## `FORMAT.name()`
Returns the name of the format (e.g. `my-awesome-format`);

## `FORMAT.options()`
Returns the unique configuration object defined by this format's properties and their settings as trafficked.

## `FORMAT.option(optionId)`
Returns the value of the specified optionId `string`.

## `FORMAT.formatId()`
Returns the unique id of the format as defined by the Specless platform (e.g. `26cT`);

## `FORMAT.creativeId()`
Returns the unique id of the tag that the format has been trafficked through in the Specless platform (e.g. `N9Rfq6`);

## `FORMAT.tagId()`
Returns the unique id of the tag that the format has been trafficked through in the Specless platform (e.g. `2kf9HBpz`);

## `FORMAT.tagFrame()`
Returns the iframe DOM element that the Specless ad tag was loaded within.

## `FORMAT.tagWindow()`
Returns the window object where the Specless ad tag was loaded (e.g. `FORMAT.tagFrame().contentWindow`);

## `FORMAT.window()`
Returns the `window` object of the parent page (if accessible) where the Specless format is loading.

## `FORMAT.document()`
Returns the `document` object of the parent page (if accessible) where the Specless format is loading.

## `FORMAT.body()`
Returns the `<body>` element of the parent page (if accessible) where the Specless format is loading.

## `FORMAT.macros()`
Returns an object containing all of the macro values that have been populated by the ad server. Macro Object contains the following keys:
- `cachebuster`
- `click`
- `click_unescaped`
- `domain`
- `impression`
- `width`
- `height`
- `destination`
- `destination_unescaped`
- `child_directed_content`
- `referrer_url`
- `server_lineitem_id`
- `server_lineitem_id`
- `server_adverister_id`
- `server_order_id`
- `server_creative_id`
- `server_adunit`
- `server_placement_id`
- `server_tagtype`
- `server_site_id`
- `server_previewmode`

## `FORMAT.macro(macroId)`
Returns the value of the specified macro id. See `FORMAT.macros()` for macro ids.

## `FORMAT.panelIsTrafficked(canvasId)`
Returns `true` if a panel is trafficked to the specified format canvas. Returns `false` otherwise.

## `FORMAT.log(message)`
Sends specially formatted message to console via `console.log` function if the tag is in a preview or development environment, but does not if in a live serving envronment.
```
FORMAT.log('This is a log.')
```

## `FORMAT.info(message)`
Sends specially formatted message to console via `console.info` function if the tag is in a preview or development environment, but does not if in a live serving envronment.
```
FORMAT.info('Here's some info.')
```

## `FORMAT.warn(message)`
Sends specially formatted warning message to console via `console.warn` function if the tag is in a preview or development environment, but does not if in a live serving envronment.
```
FORMAT.warn('This is a warning.')
```

## `FORMAT.error(message)`
Sends specially formatted error message to console via `console.error` function if the tag is in a preview or development environment, but does not if in a live serving envronment.
```
FORMAT.error('This is an error.')
```

## `FORMAT.fireTracker(name)`
Fires a reporting tracker with an id of `name`.
```
FORMAT.fireTracker('My Tracking Event');
```

## `FORMAT.reportInteraction(name)`
Reports an interaction with an id of `name`.
```
FORMAT.reportInteraction('Button Click');
```

## `FORMAT.reportDimension(name, value)`
Reports a dimension defined by the `key` as having a value of `value`. A dimension with a given key can only be reported once per session.
```
FORMAT.reportDimension('Initial Size', '3:1');
```

## `FORMAT.global(key, value)`
Sets or gets a global value for `key`. If no `value` is supposed, simply returns the current value. The global value can be read by all instances of the format on the page. If one instance of the format sets the key to a new value, all other formats values will change. Helpful for global page properties-- but do not use if different formats require different values for the global.

In addition to setting the global variable value, a data attribute of `data-specless-my-awesome-format-key` is set on the parent page's HTML element.

```
var formatInstance = FORMAT.global('instance-count');
formatInstance = Number(formatInstance) + 1;
FORMAT.global('instance-count', formatInstance);
```

```

.someElement {
	background-color: red;
}

[data-specless-my-awesome-format-format-count='2'] .someElement {
	background-color: white;
}

[data-specless-my-awesome-format-format-count='3'] .someElement {
	background-color: blue;
}

```
## `FORMAT.handleAction(name, callback)`
Defines a callback to run when the specified format action is triggered (either by the format or the creative).

```
FORMAT.handleAction('expand', function(event, resolve, reject) {
	// do stuff
	if (thisSucceded) {
		resolve()
	} else {
		reject()
	}
});
```

## `FORMAT.triggerAction(name)`
Triggers the specified format action adn then returns a promose once the action handler (defined by `FORMAT.handleAction` method) completes.

```
FORMAT.triggerAction('expand').then(function() {
 // Do stuff after the expand completes.
}).catch(function() {
	// Do stuff if the expand fails.
});
```

## `FORMAT.run(callback)`
Runs the callback and returns a promise for async functions.

```
FORMAT.run(function(resolve, reject) {
	if (thisSucceded) {
		resolve()
	} else {
		reject()
	}
}).then(function() {
	// Do some more stuff if this succeeded.
}).catch(function() {
		// Do other stuff if this failed.
});
```

## `FORMAT.injectCss(styles, location)`
Renders `styles` object as css within a `style` tag on the parent page. If `location` argument is provided as `tagWindow` then the styles will be injected within the ad server created iframe's window where the Specless tag is loaded instead of on the page.

```
FORMAT.injectCss({
	'.someElement' : {
		backgroundColor: 'blue'
	},
	'.someOtherElement' : {
		backgroundColor: 'orange',
		border: '1px solid black'
	}
});

```

## `FORMAT.loadCss(url)`
Loads a css file from the specified URL and returns a promise.
```
FORMAT.loadCss(https://mysite.com/styles.css).then(function() {
	// Do some more stuff if this succeeded.
}).catch(function() {
		// Do other stuff if this failed.
});
```

## `FORMAT.loadSiteSpecificCss(directory)`
This function loads a specific CSS file pertinent to the site that the ad is loading on. CSS files must be named as follows and placed in a directy within the format's `/assets` folder.

```
Directory Structure

/assets
	/site-css
		/thebalance_com.css
		/github_io.css
		/gizmodo_com.css
		/pcmag_com.css
```

```
FORMAT.loadSiteSpecificCss('site-css').then(function() {
	// Do some more stuff if this succeeded.
}).catch(function() {
		// Do other stuff if this failed.
});
```

## `FORMAT.getFrequencyData()`
Returns a promise that resolves once frequency cookie data is available. Passes a variable that can be used to get frequency data once the promise resolves.

```
FORMAT.getFrequencyData().then(function(frequency) {
	var freq = frequency.day().creative();
}).catch(function() {
	// Do stuff if frequency data is not available.
});
```

#### Frequency API
- `frequency.day().creative()` Returns the number of times this creative has been seen by this device in the past day.
- `frequency.day().tag()` Returns the number of times this tag has been seen by this device in the past day.
- `frequency.day().format()` Returns the number of times this format has been seen by this device in the past day.
- `frequency.week().creative()` Returns the number of times this creative has been seen by this device in the past week.
- `frequency.week().tag()` Returns the number of times this tag has been seen by this device in the past week.
- `frequency.week().format()` Returns the number of times this format has been seen by this device in the past week.
- `frequency.month().creative()` Returns the number of times this creative has been seen by this device in the past month.
- `frequency.month().tag()` Returns the number of times this tag has been seen by this device in the past month.
- `frequency.month().format()` Returns the number of times this format has been seen by this device in the past month.
- `frequency.day().site().creative()` Returns the number of times this creative has been seen by this device in the past day on the current site (as determined by the current domain).
- `frequency.day().site().tag()` Returns the number of times this tag has been seen by this device in the past day on the current site (as determined by the current domain).
- `frequency.day().site().format()` Returns the number of times this format has been seen by this device in the past day on the current site (as determined by the current domain).
- `frequency.week().site().creative()` Returns the number of times this creative has been seen by this device in the past week on the current site (as determined by the current domain).
- `frequency.week().site().tag()` Returns the number of times this tag has been seen by this device in the past week on the current site (as determined by the current domain).
- `frequency.week().site().format()` Returns the number of times this format has been seen by this device in the past week on the current site (as determined by the current domain).
- `frequency.month().site().creative()` Returns the number of times this creative has been seen by this device in the past month on the current site (as determined by the current domain).
- `frequency.month().site().tag()` Returns the number of times this tag has been seen by this device in the past month on the current site (as determined by the current domain).
- `frequency.month().site().format()` Returns the number of times this format has been seen by this device in the past month on the current site (as determined by the current domain).

## `FORMAT.Slot(slotId, settingsObject)`
Constructor that defines a new slot and returns the Slot API. A slot is a defined structure of containers that are used to style and structure ad format functionality. Slots can have one or more canvases load within the slot.

```
 var SLOT = new FORMAT.slot('my-slot', {
 		target: document.getElementById('myElement'),
 });
```

#### Slot Settings Object
All settings are optional and are show below with default values.
```
{
	target: [element], // DOM ELEMENT TO BE USED AS THE SLOT. If none is provided, the iframe element where the tag is loading is used
	state: 'initial', // Defines the initial state of the slot. can be any string
	role: 'standard', // Defines the role of the slot. can be any string (but should be one of the predefined roles per best practices)
	cascadeFrom: 'container' // Defines the parent element that should receive data attributes marking the current state and role of the slot. Values can be 'slot', 'wrapper', container', or 'parent'.
}
```

# Slot API

## `SLOT.id()`
Returns the id of the slot.

## `SLOT.slot()`
Returns the target element of the slot.

## `SLOT.slotSelector()`
Returns a string that can be used to select the slot element in css. The selector uses the session Id to namespace it uniquely to this format so that is does not conflict with any other instances of the same format on the page.

## `SLOT.slotCss(styles, state)`
Injects CSS styles on page to style the slot's slot element. Optionally, a state can be passed in to have those styles apply only when the slot's state has been set to the specified value.
```
// Default Styles for the slot
SLOT.slotCss({
	backgroundColor: 'red',
	border: '1px solid blue'
});

// Styles for when the slot's state is 'active'
SLOT.slotCss({
	backgroundColor: 'red',
	border: '1px solid blue'
}, 'active');
```

## `SLOT.wrapper()`
Returns the parent element of the `slot` element.

## `SLOT.wrapperSelector()`
Same as `SLOT.slotSelector` but for the `wrapper` element.

## `SLOT.wrapperCss(styles, state)`
Same as `SLOT.slotCss` but for the `wrapper` element.
```
// Default Styles for the slot
SLOT.wrapperCss({
	backgroundColor: 'red',
	border: '1px solid blue'
});

// Styles for when the slot's state is 'active'
SLOT.wrapperCss({
	backgroundColor: 'red',
	border: '1px solid blue'
}, 'active');
```

## `SLOT.container()`
Returns the parent element of the `wrapper` element.

## `SLOT.containerSelector()`
Same as `SLOT.slotSelector` but for the `container` element.

## `SLOT.containerCss(styles, state)`
Same as `SLOT.slotCss` but for the `container` element.
```
// Default Styles for the slot
SLOT.containerCss({
	backgroundColor: 'red',
	border: '1px solid blue'
});

// Styles for when the slot's state is 'active'
SLOT.containerCss({
	backgroundColor: 'red',
	border: '1px solid blue'
}, 'active');
```

## `SLOT.parent()`
Returns the parent element of the `container` element.

## `SLOT.parentSelector()`
Same as `SLOT.slotSelector` but for the `parent` element.

## `SLOT.parentCss(styles, state)`
Same as `SLOT.slotCss` but for the `parent` element.
```
// Default Styles for the slot
SLOT.parentCss({
	backgroundColor: 'red',
	border: '1px solid blue'
});

// Styles for when the slot's state is 'active'
SLOT.parentCss({
	backgroundColor: 'red',
	border: '1px solid blue'
}, 'active');
```

## `SLOT.role(string)`
Sets or gets the current slot role.

## `SLOT.state(string)`
Sets of gets the current slot state.

## `SLOT.size(size, addCss)`
Sets the szie by setting the 'data-specless-size' attribute on the element defined as the slot's 'cascadeFrom' element.
Fixed pixel dimensions are defined using an `x` and aspect ratios defined using `:`. For example, `300x250` or `16:9`.
If the `addCss` argument is `false` no CSS will be rendered on page to size the slot-- only the attribute will be set.
```
SLOT.size('16:9') // Sets the slot size to 16 by 9 aspect ratio and applies basic css needed to render.
SLOT.size('300x250') // Sets the slot size to 300 pixels by 250 pixels and applies basic css needed to render.
SLOT.size('4:1', false) // Sets the slot size to a 4 by 1 aspect ratio but does not apply any css.
```

## `SLOT.breakpoints(breakpointsArray, min, max, referenceEl)`
Defines a series of breakpoints and corresponding sizes.

```
// In this example, the slot will have a min width of 300 and a max width of 1500. The referenceEl is ether the current slot (so breakpoints will depend on the slot width once they have been set to fluid).

SLOT.breakpoints([
	{ // The slot will be 1:1 when smaller than 600px
		min: 0,
		max: 600,
		size: '1:1'
	},
	{ // The slot will be 2:1 when smaller larger than 601 and smaller than 901
		min: 601,
		max: 900,
		size: '2:1'
	},
	{ // The slot will be 3:1 when larger than 900px
		min: 901,
		max: 20000,
		size: '3:1'
	}], 300, 1500);

// If referenceEl is 'viewport' then the viewport width will define which breakpoints should be used.

SLOT.breakpoints([
	{ // The slot will be 1:1 when smaller than 600px
		min: 0,
		max: 600,
		size: '1:1'
	},
	{ // The slot will be 2:1 when smaller larger than 601 and smaller than 901
		min: 601,
		max: 900,
		size: '2:1'
	},
	{ // The slot will be 3:1 when larger than 900px
		min: 901,
		max: 20000,
		size: '3:1'
	}], 300, 1500, 'viewport');

```
SLOT.Canvas(canvasId, settings);
Constructs a new canvas within the slot and returns the canvas API. The canvasId MUST match the canvas id associated with the format in the Specless user interface.

```
var CANVAS = new SLOT.Canvas('primary-canvas', {})
```

#### Canvas Settings Object
All settings options are shown below and are all optional
```
{
	show: true, // should the canvas's panel show as soon as it is ready
	preload: true, // If the canvas's panel does not show immediately, should the panel load it's content before it is told to load or show
	allowVideo: true, // Should the canvas's panel be able to contain video
	allowAutoplay: true, // Should the canvas's panel be able to autoplay video
	allowCustomJs: true, // Should the canvas's panel be allowed to run custom javascript
	allowAnimation: true, // Should the canvas's panel be able to contain animated effects
	position: 'append', // Or 'prepend'-- Should the canvas be inserted before or after any other elements that are already present in the slot element
}
```

# Canvas API

## `CANVAS.element()`
Returns the canvas element.

## `CANVAS.panel()`
Returns the Specless CSF panel object/api.

```
var panel = CANVAS.panel();

panel.show().then(function() {
	//do stuff
});
```

## `CANVAS.panelContainer()`
Returns the Specless generated DIV element that contains the panel iframe.

## `CANVAS.panelFrame()`
Returns the Specless generated iframe that contains the panel document.

## `CANVAS.panelWindow()`
Returns the `window` object within the panel iframe.

## `CANVAS.parentSlot()`
Returns the SLOT API for the slot where the canvas was generated.

# CSS Selectors

The following selectors can be used in css files that are loaded onto the parent page.

**NOTE**: The following examples use a format name of 'my-format' and a slot name of 'my-slot':


#### Slot Elements:
- Slot: `[data-specless-slot*="my-format:my-slot"]`
- Slot Wrapper: `[data-specless-wrapper*="my-format:my-slot"]`
- Slot Container: `[data-specless-container*="my-format:my-slot"]`
- Slot Parent: `[data-specless-container*="my-format:my-slot"]`

#### Slot Modifier Attributes:
(These are applied to the element defined as the 'cascadeFrom' element (usually the container))
- Slot Role: `[data-specless-role="XXXXX"]`
- Slot State: `[data-specless-state="XXXXX"]`
- Slot Size: `[data-specless-size="XXXXX"]`

#### Global Format Variable
These are applied to the html element of the parent page.
- `[data-specless-my-format-global-name="XXXXX"]`
- `[data-specless-my-format-global-name]` (for globals that are true-- false will not be present);


#### Example
```
[data-specless-slot*="my-format:my-slot"] {
	opacity: 0; // Hide until page loaded
	width: 100%;
	height: 100%;
	position: relative;
}

[data-specless-wrapper*="my-format:my-slot"] {
	width: 100%;
	height: 100%;
	position: relative;
}

[data-specless-container*="my-format:my-slot"] {
	width: 100%;
	height: 90px;
	position: relative;
}

[data-specless-container*="my-format:my-slot"][data-specless-state="expanded"] {
	height: 250px;
}

[data-specless-my-format-page-loaded] [data-specless-slot*="my-format:my-slot"] {
	opacity: 1; // show once page loaded (set by FORMAT.global)
}

```