Developer Guide

This guide covers what you need to know to build awesome beat selling interfaces using the flex framework as the fabric.

Flex contains an API that you can use to create compelling new designs and implementations for your sites (or just distributable widgets) that don't require you to configure complicated back-ends. The framework allows you to focus strictly on building unique user experiences and brand value on the front-end, while all of the back-end administration, data management, and client services needed to power it remain in the cloud.

In a nutshell, the flex framework brings all of the music and features of the Beat Brokerz platform to your fingertips for you to theme and integrate as your own design specification. All the heavy lifting of fetching, loading, and managing data is done by the framework. You just pick and choose what you want to use, and where you want to use it.

How It Works

Whenever a flex app is deployed to a web page, it is linked to a configuration of music and content on the Beat Brokerz platform. Anybody with an affiliate or producer account on Beat Brokerz can create and deploy flex apps. As a developer, you can use the javascript API built into the framework, or any of the built in theming features to create page/widget designs that use this data in any way you want. The data includes playlists, beats, producer profiles, special offers, licensing options, and more. Browse the interfaces portion of our developer guide for a complete list of features available to you!

Design Objectives

Use the flex framework in new or existing projects to create theme packages, widgets, or new features for websites that are instantly integrated with the most powerful beat licensing platform available, but are then distributable in themselves as unique beat selling products without any other particular back-end dependencies.


Sandbox

Launch Sandbox Want to get hands on with the examples in this documentation? We've put together a sandbox that you can use to fiddle with the framework in real time. Use it to test new designs or run your own experiments!


API Documentation

Scripts & Libraries

The flex framework deploys very easily to web pages with only two script references. However, it has dependencies on a few other javascript libraries which are loaded asynchronously (and automatically) by the framework if they are not already present on the page.

Dependencies

If any of these libraries are already loaded on the page, flex will detect them and save time by not loading them again. If an older version of jQuery is being used than what is required, then flex will load a newer version over the top of the old one, and once flex is fully initialized, it will restore the old version using the jQuery.noConflict(true) method.


jQuery: Version: 1.8+ Documentation

jQuery Cookie: Version: 1.3+ Documentation

jPlayer: Version: 2.5+ Documentation

jPlayerPlaylist: Version: 2.3+ Documentation

Knockout.js: Version: 2.2+ Documentation

Implementation

Flex uses a customized instance of head.js to do its bootloading and provide utility functions. By loading its resources asynchronously after the DOM is ready, the flex framework is loaded on the page in a non-blocking way. You can learn how to use it to load your own resources by reading the utilities documentation.

The flex bootloader and utility class is loaded into the flexloader window namespace.

Utilities

The flex framework provides a number of important utilities via the flexloader namespace. These utility methods are made available by the flexLoader.js script when you deploy an app.


API Interface: flexloader.execute(function($, App) {})


To write code that interfaces with the flex framework, the first utility that you should get familiar with is the execute() method. The execute() method should generally be used to wrap any code which is intended to interface with the flex framework api.


Example:
flexloader.execute(function($, App) {
  // api programming...
});

Any function that you pass to the execute method will be passed two parameters. The first parameter is a reference to the jQuery instance which is being used by the framework (since it's possible to have multiple instances of jQuery running on the page). The second parameter is the application object which encapsulates the entire flex framework api, and provides access to all of the app interfaces.

Another important function of the execute() method is to ensure that the framework is fully initialized before your code is executed. Since the framework loads asynchronously after your DOM is ready, there may be a short delay before it is actually ready to receive commands. The execute() method will wait until the framework is ready to go before it executes code that is passed to it.

Alternate Usage: The execute() method also supports passing the name of a named resource (which should be added via the addResource() method) as the first parameter, and then the function to execute as the second parameter. When used in this way, the function will only be executed if (and when) the named resource is successfully loaded.

Example:
flexloader.execute('myResource', function($, App) {
  // stuff to do when "myResource" is loaded
});

// example of a flag used for conditional loading of the resource
var needMyResource = true;

if (needMyResource) {
  flexloader.addResource({
    name: 'myResource',
    src: '/path/to/resource.js'
  });
}

App Extensions: flexloader.extendApp(function($, App, config) {})


The extendApp() method is a variation of the execute() method described above, but any code passed to it will actually be executed before any code passed to the execute() method. This is true up until the point at which the app is actually initialized, at which point both the execute() method and the extendApp() method will execute code passed to them in real time.

The extendApp() method is generally used for things like adding a widget template to the framework, since you want that template to be available to other code using the standard execute() method. But it is useful in any situation where your code is more or less doing "prep" work to the framework.

Another reason to use the extendApp() method is when you are writing code that can be "autoloaded". Functions passed to this method have a third "config" parameter available to them. When you use the extendApp() method in a script file that has been autoloaded, the config parameter will contain information about the script file itself and any options that may have been passed to it. This way, it's possible for your code to load additional dependency resources (such as other script libraries or css files) on it's own, or to change it's default behavior based on the config options that it was autoloaded with.

Example:
flexloader.extendApp(function($, App, config) {
  // api programming...
});

The extendApp() method recieves the jQuery object and the App object as the first two parameters just like the execute() method. But it also receives a third parameter "config", which contains script information and options set if the script was autoloaded.


Script Autoloader: flexloader.autoload('//url.to/script.js', {options object});


The autoload() method can be used to dynamically load script resources with the ability to pass them configuration options. In order to receive configuration options, the script which is autoloaded must implement the extendApp() method of code execution within the global context (meaning, the extendApp() should not be nested within another function wrapper such as execute() ).

Example: Script With Autoload Support
// filename: scriptToLoad.js
flexloader.extendApp(function($, App, config) {
  // test if the script has been autoloaded
  if (config.autoload) {
    alert(config.options.txtMessage);
  }
});
Example: Autoload Implemented
flexloader.autoload('http://www.example.com/path/to/scriptToLoad.js', {
  txtMessage: 'Autoload Complete!'
});

When a script that implements the extendApp() method of code execution is autoloaded, the config parameter received by the function passed to extendApp() will be an object that contains information about the script itself, as well as any options that may have been passed to the autoload() method. The config parameter from the example above would look like this:

{
  autoload: true,
  script: {
    protocol: 'http', // could also be https (note there is no trailing colon)
    host: 'www.example.com', // the domain the script is on
    path: '/path/to/', // the directory path of the script (with trailing slash)
    file: 'scriptToLoad.js', // the filename of the script
    basepath: 'http://www.example.com/path/to/' // full url path to script directory
  },
  options: {
    txtMessage: 'Autoload Complete!'
  }
}

If the script is not loaded via the autoload() method (i.e. it was included via script tag in the html source or added via the addResource() method, the "config" parameter will be an empty object. That is why in the first example we checked for the "config.autoload" property to be true before attempting to access additional properties of the object. The same is true of you simply use the extendApp() method in your html body directly (the config object will be empty).


Resource Loader: flexloader.addResource({resource object})


The addResource() method adds a resource to be asynchronously loaded when the app is initialized (or immediately if the app has already been initialized). You can use it to load additional scripts or css files that your project needs. This way, you can write code that conditionally includes resources on your page without the need to hard code them into your html source.


Example:
flexloader.addResource({
  src: '/path/to/script.or.stylesheet', // required, url to the resources
  missing: function() {}, // optional, return true if resource should be loaded
  ready: function() {} // optional, function to execute when resource is ready
});

The addResource() method accepts an object as a parameter. The object passed to this method should contain a "src" property which is the url to the resource to load. There are also two additional (optional) properties that you can assign to the object also.

The "missing" property (if used) should be a function that returns true if the resource is missing and needs to be loaded. If you don't specify a function to the "missing" property, then the resource will be assumed to be missing and will load unconditionally.

The "ready" property (if used) should be a function that will be executed immediately after the resource is loaded, or if the "missing" function returns false.


Resources Ready: flexloader.ready(function() { })


You can use the ready() method to execute code once all of the resources that are in the asynchronous loading queue have been loaded. If there are no resources being loaded when this method is used, the function is executed immediately. This method is very useful after you use the addResource() method and you want to make sure that the resource(s) that you just added are ready before your code execution continues.

Example:
flexloader.execute(function($, App) {
  flexloader.addResource({ src: '/path/to/script.js' });
  flexloader.addResource({ src: '/path/to/style.css' });
  flexloader.ready(function() {
    // now you can write code that is dependent on the script.js and style.css resources!
  });
});

App Ready: App.ready(function() {})


The App.ready() method allows you to write code that needs access to baseline app data (such as playlist info, producer info, discount info, etc). This method ensures that the baseline app data is ready before your code is executed.

In contrast, the flexloader.execute() method will execute code immediately after the framework is ready to receive commands, but the App.ready() method will wait until the primary application data is loaded before execution. This means that app data may not be available immediately inside the flexloader.execute() method (since app data is loaded sequentially after the framework), but it will always be available to code passed to the App.ready() method.


Example:
flexloader.execute(function($, App) {
  App.ready(function() {
    // things to do when primary app data is ready
  });
});

App Final: App.final(function() {})


The App.final() method is similar to the App.ready() method, in that it allows you to write code that operates on app data (such as playlists, producers, discounts, etc). The difference is that this method waits until ALL initial loading activity is complete before your code is executed.

In comparison, the App.ready() method will execute code immediately after the baseline app data is available (such as playlist info, but not necessarily the initial beats that are in the playlist), but the App.final() method will wait until all of the initial data loading has been finalized. This way you can be sure that every bit of data has been queried for the app before your code is executed.


Example:
flexloader.execute(function($, App) {
  App.final(function() {
    // things to do when all app data is fully loaded. This is a good time to remove a page loading screen... etc.
  });
});

Interfaces

Music & Playlists

Shopping Cart

Producers

Discounts

Content

General App

View Model

Events

The flex framework implements an observer design pattern for the dispatch and handling of events within the app. There are a variety of baseline events that get triggered within the framework on a regular basis that you can easily add your own handlers to. And creating your own events that other components can listen for and respond to is just as easy.

Events allow you to write code that stays synchronized with the application as the user interacts with it. By writing event driven code, you can be sure that your features will work with all of the other moving parts in the application.

Handle Events: App.on('event-name', Handler)

Using the App.on() method, you can write your own handler code that responds to any event which is triggered by the application. Depending on the event triggered, any number of parameters may be passed to your handler. Refer to the documentation on the specific event for more information about the parameters available.

flexloader.execute(function($, App) {

  App.on('event-name', function(param1, param2) {
    var event = this;
    // do something to handle event...
    event.myMessage = param1 + ' ' + param2;
  });

});

Whenever you write a handler function for an event with the App.on() method, the this keyword is a reference to the event object that will ultimately be returned to the caller of the event. This object is shared among all of the handlers that are attached to the event.

Trigger Events: App.trigger('event-name', param1, param2)

You can trigger your own app wide events by using the App.trigger() method. The first parameter sent should be the name of the event, and each parameter after that will be passed to any handlers attached to your event name via the App.on() method.

flexloader.execute(function($, App) {

  var param1 = "Hello";
  var param2 = "World";
  var eventData = App.trigger('event-name', param1, param2);
  alert(eventData.myMessage || 'No Message!');

});

The return value of the App.trigger() method will be an event object whose properties (if any) have been set by the handlers attached to the event. If the attached handlers do nothing to modify the event object properties (or if there are no handlers attached to the event), then the return result of the App.trigger() method will simply be an empty object.


Events Documentation

Event Propagation

Each event in the flex framework is internally divided into "stages" which allows for a more controlled progression of processing for the event. The idea is that a particular handler may need to control, affect, or monitor the behavior of other handlers that may also be attached to the event. In such cases, it's necessary to ensure that your handlers are executed at certain "stages" of the event.

Advanced Usage: App.on('event-name', 'stage', Handler)

When you attach a handler to an event, you can specify a specific stage of the event you want your handler to execute in. This allows handlers to be prioritized (and even bypassed) during event propagation. When using the shorter App.on('event-name', Handler) method to attach a handler to an event (without specifying an explicit stage), flex will internally assign it to the 'process' stage by default.

Basic Event Stages

When an event is triggered by the flex framework, handlers for the event are executed in groups called "stages". By default, every event will follow a standard progression of event stages:

  • before : Do any preliminary setup stuff for the data processing.
  • process : Do regular data processing for the event during this stage.
  • render : Do manipulations and updates to the user interface during this stage.
  • after : Do any post-processing stuff for the event.

When an event is triggered, flex will begin to execute all handlers attached to the event; stage by stage. When executing handlers for a given event stage, they are executed in the order they were attached to that stage.

Custom Event Stages

Events can be made to progress through any number of customized stages in their life cycle. You may want to design your own events with custom stages to better serve the purpose of your event. For example, you may want your event to include an "init" stage that you can use to prepare the event object in your own way prior to the standard 'before', 'process', 'render', and 'after' stages. To create a custom event sequence, use the App.sequence() method.

Example:
flexloader.execute(function($, App) {
  App.sequence('custom-event-name', [ 'init', 'before', 'process', 'render', 'after' ]);
});

Customization of event stages are done "per event". The first parameter used in the method is the name of the event to re-sequence, and the second parameter is an array with the name of the different stages to implement (in the order that they should execute).

If you want to return the current sequence for a given event, call the App.sequence('event-name') method without the second "sequence" parameter. The return result will be an array of the current stages implemented for the event.

Regardless of the stages that you use and the order in which you use them, it is wise to implement a 'process' stage for your event so that handlers attached to your event using the shorter
App.on('custom-event-name', Handler) method will not be orphaned (since they are internally assigned to the 'process' stage by the framework).

Controlling Event Propagation

During event propagation, the event object has some internal methods available that can be used to control the flow of the event processing. With these methods, you can fork processing to handlers attached to another event, bypass certain stages of the current event, or even halt further processing of the event altogether.

Forking: event.fork(String : Array)

Whenever you use the event.fork() method in an event handler, you will give it either a single event name (string), or a list of event names (array). The fork events will be triggered immediately after the current stage of processing for the current event is complete, and before the next stage of processing for the current event begins. The event object will be shared with the forked events, and the arguments passed to the forked events will be the same arguments that were passed into the current event.

By forking an event, you are essentially adding a whole new series of stages into the current event flow since the forked event will also have its own full set of event stages that it will run through.

Bypassing: event.bypass(String : Array)

Whenever you use the event.bypass() method in an event handler, you will give it either the name of a single stage (string), or a list of stages (array). The stages given will then be bypassed in the current event as the progression moves forward.

Example:
flexloader.execute(function($, App) {

  /**
   *  Handle the "cart show" event and do our own
   *  thing if the cart is empty :p
   */

  App.on('bbflex-cart-show', 'before', function() {

    // access the event object
    var event = this;

    if (App.Cart.data().items.length == 0) {
      alert('Nothing in your shopping cart. Time to get shopping!');
      event.bypass('render');
    }

  });

});

Halting: event.halt()

You can use the event.halt() method in an event handler to stop any additional processing of the current event for the rest of this stage and any stages to follow. However, any events set with event.fork() will still be forked when the handler is finished, and if the event being halted was a fork of another event, the event that it was forked from will still continue its own propagation.

Example:
flexloader.execute(function($, App) {

  // access the event object
  var event = this;

  /**
   *  Halt the standard "playlists show" event and
   *  fork the event to your own handlers
   */

  App.on('bbflex-playlists-show', 'before', function() {
    event.halt();
    event.fork('mycustom-playlists-show');
  });

  /**
   *  Handle your custom "playlists show" event
   */

  App.on('mycustom-playlists-show', 'render', function() {
    // do your own stuff to show the playlists to the user
    alert('My own implementation of the show playlists feature...');
  });

});

Advanced Triggering

If you want to trigger only certain stages within an event, you can do that by specifying the stages to be executed (seperated by colons) after the event name when using the App.trigger() method. This format can also be used when specifying event names using the event.fork() method.

Example:
flexloader.execute(function($, App) {

  // trigger only the 'process' and 'render' stages of 'event-name'
  App.trigger('event-name:process:render', param);

});

Event Catalog

Many different events are triggered internally by the framework as the user interacts with the application. You can use them to write event driven code to keep your features synchronized with the current state of the application. Other third party widgets may also create their own events that you can interact with, however, the following catalog of events refer only to the core events provided by the framework.

Sandbox

Launch Sandbox Want to get hands on with the examples in this documentation? We've put together a sandbox that you can use to fiddle with the framework in real time. Use it to test new designs or run your own experiments!


Music Events

Music Change: bbflex-nowplaying : (media)

Event Params:

  • media: object : A beat object representing the new song

This event is triggered each time the active beat in the application is changed, either by the playlist advancing to the next song, or through an API method that selects a new song to be active.

Example:
flexloader.execute(function($, App) {
  App.on('bbflex-nowplaying', function(media) {
    alert('New Song: ' + media.title);
  });
});

Music Play: bbflex-play : (event, player)

Event Params:

  • event : object : A jPlayer event object
  • player : jQuery instance : The jPlayer instance that generated the event

This event is triggered each time the music player begins playing a new song, or resumes playing a paused song.

Example:
flexloader.execute(function($, App) {
  App.on('bbflex-play', function(event, player) {
    var media = event.jPlayer.status.media;
    alert('Playing: ' + media.title);
  });
});

Music Pause: bbflex-pause : (event, player)

Event Params:

  • event : object : A jPlayer event object
  • player : jQuery instance : The jPlayer instance that generated the event

This event is triggered each time the music player is paused.

Example:
flexloader.execute(function($, App) {
  App.on('bbflex-pause', function(event, player) {
    var media = event.jPlayer.status.media;
    alert('Paused: ' + media.title);
  });
});

Volume Change: bbflex-volumechange : (event, player)

Event Params:

  • event : object : A jPlayer event object
  • player : jQuery instance : The jPlayer instance that generated the event

This event is triggered each time the volume of the music player is changed.

Example:
 

Time Update: bbflex-timeupdate : (event, player)

Event Params:

  • event : object : A jPlayer event object
  • player : jQuery instance : The jPlayer instance that generated the event

This event is triggered continually as the play position changes for the currently playing song in the music player.

Example:
 

Music Seeking: bbflex-seeking : (event, player)

Event Params:

  • event : object : A jPlayer event object
  • player : jQuery instance : The jPlayer instance that generated the event

Example:
 

Loading Progress: bbflex-progress : (event, player)

Event Params:

  • event : object : A jPlayer event object
  • player : jQuery instance : The jPlayer instance that generated the event

This event is triggered continually as new media is being downloaded to the music player.

Example:
 

Repeat Changed: bbflex-repeat : (event, player)

Event Params:

  • event : object : A jPlayer event object
  • player : jQuery instance : The jPlayer instance that generated the event

This event is triggered each time the "repeat" feature of the music player is changed.

Example:
 

Music Ended: bbflex-ended : (event, player)

Event Params:

  • event : object : A jPlayer event object
  • player : jQuery instance : The jPlayer instance that generated the event

This event is triggered when the music player reaches the end of a song.

Example:
 

Music Error: bbflex-jplayer-error : (event, player)

Event Params:

  • event : object : A jPlayer event object
  • player : jQuery instance : The jPlayer instance that generated the event

Example:
 

Playlist Events

Showing Playlists: bbflex-playlists-show

Event Params:

  • None

This event is triggered when the framework is going to show a dialog to the user to select from the available playlists.

Example:
 

Playlist Reset: bbflex-playlist-reset : (id)

Event Params:

  • id: String : The ID of the playlist which has been reset

This event is triggered immediately after a playlist is initialized by the framework, or after a playlist is reset by the framework in order to begin reloading the playlist media. Either way, information about the playlist is available immediately via the playlists interface, but the playlist will have been reset to an empty media array.

Example:
 

Playlist Updated: bbflex-playlist-updated : (id, media, index)

Event Params:

  • id: String : The ID of the playlist which has been updated
  • media: Object : The media object of the beat that was added
  • index: Integer : The index of the beat in the playlists media array

This event is triggered every time a playlist is updated with a new beat. It is triggered once for each and every time a new beat is added to the playlist.

Example:
 

Playlist Changed: bbflex-playlist-changed : (id)

Event Params:

  • id: String : The ID of the new active playlist

This event is triggered by the framework any time the active playlist in the application is changed.

Example:
 

Widget API

The widget API allows you to neatly pre-package custom features or design interfaces into a "widget" component which can then be re-used on the fly any number of times by using standard shortcodes. Widgets can also be distributed via their own script file and loaded by other apps that want to use them.