QNX CAR Platform for Infotainment 2.1 -- JavaScript API (Cordova)

/**
 * @module car.mediaplayer
 * @description Media playback, browse, and search API.
 */
 
/*
 * @author lgreenway
 * $Id: client.js 6694 2013-06-26 20:25:01Z lgreenway@qnx.com $
 */

var _ID = require("./manifest.json").namespace,
	_callback = require('lib/callback'),
	Event = require('./enum/Event'),
	MediaNodeType = require('./enum/MediaNodeType'),
	PlayerStatus = require('./enum/PlayerStatus'),
	MediaSourceEvent = require('./enum/MediaSourceEvent'),
	MediaSourceType = require('./enum/MediaSourceType'),
	TrackSessionEvent = require('./enum/TrackSessionEvent'),
	RepeatMode = require('./enum/RepeatMode'),
	ShuffleMode = require('./enum/ShuffleMode');

/**
 * The car.mediaplayer MediaPlayer instance contstructor function. Opens the specified player and returns an
 * instance of the MediaPlayer object, used to perform actions on the media player and receive update events
 * through watchers.
 * @name Mediaplayer
 * @param {String} playerName The player name to open. If the player does not exist it will automatically be created.
 * @returns {MediaPlayer} The car.mediaplayer.MediaPlayer instance.
 * @memberOf module:car.mediaplayer
 * @constructor
 * @example
 *
 * // Instantiate a media player object, specifying the player name
 * var mediaPlayer = new car.mediaplayer.MediaPlayer('playerName');
 *
 *
 *
 * @example REST
 *
 * Request:
 * http://<car-ip>/car.mediaplayer/open?playerName=playerName
 *
 * Success Response:
 * {
 *		"code": 1,
 *		"data": true
 * }
 *
 * Error Response:
 * {
 *		code: -1,
 *		msg: "An error has occurred"
 * }
 */
var MediaPlayer = function(playerName) {
	/**
	 * Reference to this object instance.
	 * @private
	 */
	var self = this;
	
	/**
	 * Opens the specified player name.
	 * @param {String} name The player name to open. If the player does not exist it will automatically be created.
	 * @returns {Boolean} True if the player was opened successfully, False if not.
	 * @private
	 */
	var open = function(playerName) {
		console.log('car.mediaplayer/client.js::open', playerName);
		return window.webworks.execSync(_ID, 'open', { playerName: playerName });
	};
	
	/**
	 * Closes the specified player name.
	 * @param {String} name The player name to close.
	 * @returns {Boolean} True if the player was closed successfully, False if not.
	 * @private
	 */
	var close = function(playerName) {
		console.log('car.mediaplayer/client.js::close', playerName);
		return window.webworks.execSync(_ID, 'close', { playerName: playerName });
	};

	/**
	 * Return the list of available media sources connected to the device
	 *
	 * @param {Function} success The success callback handler.
	 * @param {Function} [error] The error callback handler.
	 * @memberOf module:car.mediaplayer.Mediaplayer
	 * @method getMediaSources	 
	 * @example
	 * // Define your callback function(s)
	 * function successCallback(mediaSources) {
	 * 	// Iterate through all of the media sources
	 * 	for (var i=0; i<mediaSources.length; i++) {
	 * 		console.log("media source id = " + mediaSources[i].id + "\n" +
	 * 					"media source uid = " + mediaSources[i].uid + "\n" +
	 * 					"media source name = " + mediaSources[i].name + "\n" +
	 * 					"media source type = " + mediaSources[i].type + "\n" +
	 * 					"media source ready = " + mediaSources[i].ready);
	 * 		console.group("media source capabilities");
	 * 		console.log("play = " + mediaSources[i].capabilities.play + "\n" +
	 * 					"pause = " + mediaSources[i].capabilities.pause + "\n" +
	 * 					"stop = " + mediaSources[i].capabilities.stop + "\n" +
	 * 					"next = " + mediaSources[i].capabilities.next + "\n" +
	 * 					"previous = " + mediaSources[i].capabilities.previous + "\n" +
	 * 					"seek = " + mediaSources[i].capabilities.seek + "\n" +
	 * 					"shuffle = " + mediaSources[i].capabilities.shuffle + "\n" +
	 * 					"repeatOne = " + mediaSources[i].capabilities.repeatOne + "\n" +
	 * 					"repeatAll = " + mediaSources[i].capabilities.repeatAll + "\n" +
	 * 					"metadata = " + mediaSources[i].capabilities.metadata + "\n" +
	 * 					"search = " + mediaSources[i].capabilities.search + "\n" +
	 * 					"playbackRate = " + mediaSources[i].capabilities.playbackRate);
	 * 		console.groupEnd();
	 * 	}
	 * }
	 * 
	 * function errorCallback(error) {
	 *		console.log(error.code, error.msg);
	 * }
	 *
	 * //call the method
	 * car.mediaplayer.getMediaSources(successCallback, errorCallback);
	 *
	 *
	 *
	 * @example REST
	 *
	 * Request:
	 * http://<car-ip>/car.mediaplayer/getMediaSources
	 *
	 * Success Response:
	 * {
	 *		code: 1,
	 *		data: [
	 *			{
	 *				id: 0,
	 *				uid: "dbmme",
	 *				name: "Juke Box",
	 *				type: 1,
	 *				capabilities: {
	 *					play: true,
	 *					pause: true,
	 *					stop: true,
	 *					next: true,
	 *					previous: true,
	 *					seek: true,
	 *					shuffle: true,
	 *					repeatOne: true,
	 *					repeatAll: true,
	 *					metadata: true,
	 *					search: true,
	 *					playbackRate: true
	 *				}
	 *			}, {
	 *				id: 1,
	 *				uid: "0",
	 *				name: "Bluetooth Phone",
	 *				type: 4096,
	 *				capabilities: {
	 *					play: true,
	 *					pause: true,
	 *					stop: true,
	 *					next: false,
	 *					previous: false,
	 *					seek: false,
	 *					shuffle: false,
	 *					repeatOne: false,
	 *					repeatAll: false,
	 *					metadata: true,
	 *					search: false,
	 *					playbackRate: false
	 *				}
	 *			}
	 *		]
	 * }
	 *
	 * Error Response:
	 * {
	 *		code: -1,
	 *		msg: "An error has occurred"
	 * }
	 */
	self.getMediaSources = function(success, error) {
		console.log('car.mediaplayer/client.js::getMediaSources');
		window.webworks.exec(success, error, _ID, 'getMediaSources', null, false);
	};
	
	/**
	 * Browse a media source for media
	 * @param {Number} mediaSourceId The ID of the media source.
	 * be retrieved.
	 * @param {Function} success The success callback handler.
	 * @param {Function} [error] The error callback handler.
	 * @param {String} [mediaNodeId=null] The ID of the media node to browse. If omitted or null, the root node will
	 * @param {Number} [limit=-1] The maximum number of records to retrieve. If omitted or negative,
	 * all records will be retrieved.
	 * @param {Number} [offset=0] The offset at which to start retrieving records. If omitted or negative,
	 * offset will be 0.
	 * @memberOf module:car.mediaplayer.Mediaplayer
	 * @method browse		 
	 * @example
	 *
	 * // Define your callback function(s)
	 * function successCallback(mediaNodes) {
	 * 	// Iterate through all of the node results
	 * 	for (var i=0; i<mediaNodes.length; i++) {
	 * 		console.log("media node id = " + mediaNodes[i].id + "\n" +
	 * 					"media node name = " + mediaNodes[i].name + "\n" +
	 * 					"media node url = " + mediaNodes[i].url + "\n" +
	 * 					"media node type = " + mediaNodes[i].type + "\n" +
	 * 					"media node count = " + mediaNodes[i].count + "\n");
	 * 	}
	 * }
	 * 
	 * function errorCallback(error) {
	 * 	console.log(error.code, error.msg);
	 * }
	 * 
	 * // Instantiate a media player object, specifying the player name
	 * var mediaPlayer = new car.mediaplayer.MediaPlayer('playerName');
	 * 
	 * // Browse the media source from its root
	 * mediaPlayer.browse(0, successCallback, errorCallback, null, 10, 0);
	 *
	 *
	 *
	 * @example REST
	 *
	 * Request:
	 * http://<car-ip>/car.mediaplayer/browse?mediaSourceId=0&limit=10&offset=0
	 *
	 * Success Response:
	 * {
	 *		code: 1,
	 *		data: [
	 *		{
	 *			"id": "/artists-2",
	 *			"name": "Artists",
	 *			"url": null,
	 *			"type": 1,
	 *			"count": -1
	 *		},
	 *		{
	 *			"id": "/albums-5",
	 *			"name": "Albums",
	 *			"url": null,
	 *			"type": 1,
	 *			"count": -1
	 *		},
	 *		{
	 *			"id": "/genres-8",
	 *			"name": "Genres",
	 *			"url": null,
	 *			"type": 1,
	 *			"count": -1
	 *		},
	 *		{
	 *			"id": "/songs-7",
	 *			"name": "Songs",
	 *			"url": null,
	 *			"type": 1,
	 *			"count": -1
	 *		},
	 *		{
	 *			"id": "/videos-10",
	 *			"name": "Videos",
	 *			"url": null,
	 *			"type": 1,
	 *			"count": -1
	 *		}
	 *	]
	 * }
	 *
	 * Error Response:
	 * {
	 *		code: -1,
	 *		msg: "An error has occurred"
	 * }
	 */
	self.browse = function(mediaSourceId, success, error, mediaNodeId, limit, offset) {
		console.log('car.mediaplayer/client.js::browse', mediaSourceId, mediaNodeId, limit, offset);
		var args = {
				mediaSourceId: mediaSourceId,
				mediaNodeId: typeof mediaNodeId === 'string' && mediaNodeId.trim() !== '' ? mediaNodeId : null,
				limit: typeof limit === 'number' ? limit : -1,
				offset: typeof offset === 'number' ? offset : 0
		};
		window.webworks.exec(success, error, _ID, 'browse', args, false);
	};
	
	/**
	 * Search for media in a specific media source.
	 * @param {Number} mediaSourceId The ID of the media source.
	 * @param {String} searchTerm The term to search for.
	 * @param {Function} success The success callback handler.
	 * @param {Function} [error] The error callback handler.
	 * @param {String} [mediaNodeId=null] The ID of the media node from which to search.
	 * @param {Number} [limit=-1] The maximum number of records to retrieve. If omitted or negative,
	 * all records will be retrieved.
	 * @param {Number} [offset=0] The offset at which to start retrieving records. If omitted or negative,
	 * offset will be 0.
	 * @memberOf module:car.mediaplayer.Mediaplayer
	 * @method search	 
	 * @example
	 *
	 * // Define your callback function(s)
	 * function successCallback(mediaNodes) {
	 * 	// Iterate through all of the node results
	 * 	for (var i=0; i<mediaNodes.length; i++) {
	 * 		console.log("media node id = " + mediaNodes[i].id + "\n" +
	 * 					"media node name = " + mediaNodes[i].name + "\n" +
	 * 					"media node url = " + mediaNodes[i].url + "\n" +
	 * 					"media node type = " + mediaNodes[i].type + "\n" +
	 * 					"media node count = " + mediaNodes[i].count + "\n");
	 * 	}
	 * }
	 * 
	 * function errorCallback(error) {
	 * 	console.log(error.code, error.msg);
	 * }
	 * 
	 * // Instantiate a media player object, specifying the player name
	 * var mediaPlayer = new car.mediaplayer.MediaPlayer('playerName');
	 * 
	 * // Search the media source from its root
	 * mediaPlayer.search(0, 'ong', successCallback, errorCallback, null, 10, 0);	 *
	 *
	 *
	 *
	 * @example REST
	 *
	 * Request:
	 * http://<car-ip>/car.mediaplayer/search?mediaSourceId=0&searchTerm=ong&limit=10&offset=0
	 *
	 * Success Response:
	 * {
	 * 	"code": 1,
	 * 	"data": [
	 * 		{
	 * 			"id": "/artists/11-3",
	 * 			"name": "AllisonGray",
	 * 			"url": null,
	 * 			"type": 1,
	 * 			"count": 1
	 * 		},
	 * 		{
	 * 			"id": "/songs/15",
	 * 			"name": "Sarasong",
	 * 			"url": "file:///accounts/1000/shared/music/Sarasong.mp3",
	 * 			"type": 2,
	 * 			"count": -1
	 * 		}
	 * 	]
	 * }
	 *
	 * Error Response:
	 * {
	 *		code: -1,
	 *		msg: "An error has occurred"
	 * }
	 */
	self.search = function(mediaSourceId, searchTerm, success, error, mediaNodeId, limit, offset) {
		console.log('car.mediaplayer/client.js::search', arguments);
		var args = {
				mediaSourceId: mediaSourceId,
				searchTerm: searchTerm,
				mediaNodeId: typeof mediaNodeId === 'string' && mediaNodeId.trim() !== '' ? mediaNodeId : null,
				limit: typeof limit === 'number' ? limit : -1,
				offset: typeof offset === 'number' ? offset : 0
		};
		window.webworks.exec(success, error, _ID, 'search', args, false);
	};
	
	/**
	 * Creates a new track session
	 * @param {Number} mediaSourceId The ID of the media source.
	 * @param {Function} success The success callback handler.
	 * @param {Function} [error] The error callback handler.
	 * @param {?String} [mediaNodeId=null] The media node ID on which to base the track session.
	 * @param {Number} [index=0] The index of the item within the track session to set as current after creation.
	 * @param {Number} [limit=-1] The maximum number of media nodes to add to the track session. A limit of -1
	 * indicates no limit.
	 * @param {Number} [offset=0] The offset within the specified media node at which to start building the track
	 * session.
	 * @memberOf module:car.mediaplayer.Mediaplayer
	 * @method createTrackSession	 
	 * @example
	 *
	 * // Define your callback function(s)
	 * function successCallback(result) {
	 * 	console.log("track session id = " + result.trackSessionId + "\n");
	 * 	
	 * 	// Play the new track session
	 * 	mediaPlayer.play();
	 * }
	 * 
	 * function errorCallback(error) {
	 * 	console.log(error.code, error.msg);
	 * }
	 * 
	 * // Instantiate a media player object, specifying the player name
	 * var mediaPlayer = new car.mediaplayer.MediaPlayer('playerName');
	 * 
	 * // Create a track session from a folder node ID, setting the third track as first to play
	 * mediaPlayer.createTrackSession(0, successCallback, errorCallback, '/songs-7', 2);
	 *
	 *
	 *
	 * @example REST
	 *
	 * Request:
	 * http://<car-ip>/car.mediaplayer/createTrackSession?mediaSourceId=0&mediaNodeId=/songs-7&index=2
	 *
	 * Success Response:
	 * {
	 * 	"code": 1,
	 * 	"data": {
	 * 		"trackSessionId": 10
	 * 	}
	 * }
	 *
	 * Error Response:
	 * {
	 *		code: -1,
	 *		msg: "An error has occurred"
	 * }
	 */
	self.createTrackSession = function(mediaSourceId, success, error, mediaNodeId, index, limit, offset) {
		console.log('car.mediaplayer/client.js::createTrackSession', arguments);
		var args = {
				mediaSourceId: mediaSourceId,
				mediaNodeId: mediaNodeId,
				index: index,
				limit: limit,
				offset: offset
		};
		window.webworks.exec(success, error, _ID, 'createTrackSession', args, false);
	};
	
	/**
	 * Destroys an existing track session.
	 * @param {Number} trackSessionId The track session ID.
	 * @memberOf module:car.mediaplayer.Mediaplayer
	 * @method destroyTrackSession	 
	 * @example
	 *
	 * // Instantiate a media player object, specifying the player name
	 * var mediaPlayer = new car.mediaplayer.MediaPlayer('playerName');
	 * 
	 * // Destroy the track session by its ID
	 * mediaPlayer.destroyTrackSession(10);
	 *
	 *
	 *
	 * @example REST
	 *
	 * Request:
	 * http://<car-ip>/car.mediaplayer/destroyTrackSession?trackSessionId=10
	 *
	 * Success Response:
	 * {
	 * 	"code": 1
	 * }
	 *
	 * Error Response:
	 * {
	 *		code: -1,
	 *		msg: "An error has occurred"
	 * }
	 */
	self.destroyTrackSession = function(trackSessionId) {
		console.log('car.mediaplayer/client.js::destroyTrackSession', trackSessionId);
		var args = { trackSessionId: trackSessionId };
		window.webworks.execAsync(_ID, 'destroyTrackSession', args);
	};
	
	/**
	 * Retrieves the current track session information.
	 * @param {Function} success The success callback handler.
	 * @param {Function} [error] The error callback handler.
	 * @memberOf module:car.mediaplayer.Mediaplayer
	 * @method getTrackSessionInfo
	 * @example
	 *
	 * // Define your callback function(s)
	 * function successCallback(result) {
	 * 	console.log("track session id = " + result.trackSessionId + "\n" +
	 * 				"track session length = " + result.length);
	 * }
	 * 
	 * function errorCallback(error) {
	 * 	console.log(error.code, error.msg);
	 * }
	 * 
	 * // Instantiate a media player object, specifying the player name
	 * var mediaPlayer = new car.mediaplayer.MediaPlayer('playerName');
	 * 
	 * // Get the current track session info
	 * mediaPlayer.getTrackSessionInfo(successCallback, errorCallback);
	 *
	 *
	 *
	 * @example REST
	 *
	 * Request:
	 * http://<car-ip>/car.mediaplayer/getTrackSessionInfo
	 *
	 * Success Response:
	 * {
	 * 	"code": 1,
	 * 	"data": {
	 * 		"trackSessionId": 10,
	 * 		"length": 17
	 * 	}
	 * }
	 *
	 * Error Response:
	 * {
	 *		code: -1,
	 *		msg: "An error has occurred"
	 * }
	 */
	self.getTrackSessionInfo = function(success, error) {
		console.log('car.mediaplayer/client.js::getTrackSessionInfo');
		window.webworks.exec(success, error, _ID, 'getTrackSessionInfo', null, false);
	};
	
	/**
	 * Retrieves media from the current track session
	 * @param {Function} success The success callback handler.
	 * @param {Function} [error] The error callback handler.
	 * @param {Number} [limit=-1] The maximum number of records to retrieve. If omitted or negative,
	 * all records will be retrieved.
	 * @param {Number} [offset=0] The offset at which to start retrieving records. If omitted or negative,
	 * offset will be 0.
	 * @memberOf module:car.mediaplayer.Mediaplayer
	 * @method getTrackSessionItems	 
	 * @example
	 *
	 * function successCallback(mediaNodes) {
	 * 	// Iterate through all of the node results
	 * 	for (var i=0; i<mediaNodes.length; i++) {
	 * 		console.log("media node id = " + mediaNodes[i].id + "\n" +
	 * 					"media node name = " + mediaNodes[i].name + "\n" +
	 * 					"media node url = " + mediaNodes[i].url + "\n" +
	 * 					"media node type = " + mediaNodes[i].type + "\n" +
	 * 					"media node count = " + mediaNodes[i].count + "\n");
	 * 	}
	 * }
	 * 
	 * function errorCallback(error) {
	 * 	console.log(error.code, error.msg);
	 * }
	 * 
	 * // Instantiate a media player object, specifying the player name
	 * var mediaPlayer = new car.mediaplayer.MediaPlayer('playerName');
	 * 
	 * // Get the third through fifth track session items
	 * mediaPlayer.getTrackSessionItems(successCallback, errorCallback, 3, 2);
	 *
	 *
	 *
	 * @example REST
	 *
	 * Request:
	 * http://<car-ip>/car.mediaplayer/getTrackSessionItems?limit=3&offset=2
	 *
	 * Success Response:
	 * {
	 * 	"code": 1,
	 * 	"data": [
	 * 		{
	 * 			"id": "/songs/3",
	 * 			"name": "Funktional Love ft. Ruchama",
	 * 			"url": "file:///accounts/1000/shared/music/Funktional Love ft. Ruchama.mp3",
	 * 			"type": 2,
	 * 			"count": -1
	 * 		},
	 * 		{
	 * 			"id": "/songs/4",
	 * 			"name": "Hero",
	 * 			"url": "file:///accounts/1000/shared/music/Hero.mp3",
	 * 			"type": 2,
	 * 			"count": -1
	 * 		},
	 * 		{
	 * 			"id": "/songs/6",
	 * 			"name": "In Her Smile",
	 * 			"url": "file:///accounts/1000/shared/music/In Her Smile.mp3",
	 * 			"type": 2,
	 * 			"count": -1
	 * 		}
	 * 	]
	 * }
	 *
	 * Error Response:
	 * {
	 *		code: -1,
	 *		msg: "An error has occurred"
	 * }
	 */
	self.getTrackSessionItems = function(success, error, limit, offset) {
		console.log('car.mediaplayer/client.js::getTrackSessionItems', limit, offset);
		var args = {
				limit: typeof limit === 'number' ? limit : -1,
				offset: typeof offset === 'number' ? offset : 0
		};
		window.webworks.exec(success, error, _ID, 'getTrackSessionItems', args, false);
	};

	/**
	 * Retrieves the currently playing track information
	 * @param {Function} success The success callback handler.
	 * @param {Function} [error] The error callback handler.
	 * @memberOf module:car.mediaplayer.Mediaplayer	 
	 * @method getCurrentTrack
	 * @example
	 *
	 * function successCallback(trackInfo) {
	 * 	console.log("track info media source ID = " + trackInfo.mediaSourceId + "\n" +
	 * 				"track info track session index = " + trackInfo.index);
	 * 
	 * 	console.group("track info media node");
	 * 	console.log("id = " + trackInfo.mediaNode.id + "\n" +
	 * 				"name = " + trackInfo.mediaNode.name + "\n" +
	 * 				"url = " + trackInfo.mediaNode.url + "\n" +
	 * 				"type = " + trackInfo.mediaNode.type + "\n" +
	 * 				"count = " + trackInfo.mediaNode.count + "\n");
	 * 	console.groupEnd();
	 * 
	 * 	console.group("track info metadata");
	 * 	console.log("title = " + trackInfo.metadata.title + "\n" +
	 * 				"duration = " + trackInfo.metadata.duration + "\n" +
	 * 				"artwork = " + trackInfo.metadata.artwork + "\n" +
	 * 				"artist = " + trackInfo.metadata.artist + "\n" +
	 * 				"album = " + trackInfo.metadata.album + "\n" +
	 * 				"genre = " + trackInfo.metadata.genre + "\n" +
	 * 				"disc = " + trackInfo.metadata.disc + "\n" +
	 * 				"track = " + trackInfo.metadata.track + "\n" +
	 * 				"width = " + trackInfo.metadata.width + "\n" +
	 * 				"height = " + trackInfo.metadata.height);
	 * 	console.groupEnd();
	 * }
	 * 
	 * function errorCallback(error) {
	 * 	console.log(error.code, error.msg);
	 * }
	 * 
	 * // Instantiate a media player object, specifying the player name
	 * var mediaPlayer = new car.mediaplayer.MediaPlayer('playerName');
	 * 
	 * // Get the current track information
	 * mediaPlayer.getCurrentTrack(successCallback, errorCallback);
	 *
	 *
	 *
	 * @example REST
	 *
	 * Request:
	 * http://<car-ip>/car.mediaplayer/getCurrentTrack
	 *
	 * Success Response:
	 * {
	 * 	"code": 1,
	 * 	"data": {
	 * 		"index": 0,
	 * 		"mediaSourceId": 0,
	 * 		"mediaNode": {
	 * 			"id": "/songs/1",
	 * 			"name": "Bo Bo's Groove",
	 * 			"url": "file:///accounts/1000/shared/music/Bo Bo's Groove.mp3",
	 * 			"type": 2,
	 * 			"count": -1
	 * 		},
	 * 		"metadata": {
	 * 			"title": "Bo Bo's Groove",
	 * 			"duration": 318411,
	 * 			"artwork": "file:///apps/mediasources/imagecache//mme/2/original",
	 * 			"artist": "tomprincipato",
	 * 			"album": "Raising The Roof!",
	 * 			"genre": "rock",
	 * 			"disc": 0,
	 * 			"track": 0,
	 * 			"width": -1,
	 * 			"height": -1
	 * 		}
	 * 	}
	 * }
	 *
	 * Error Response:
	 * {
	 *		code: -1,
	 *		msg: "An error has occurred"
	 * }
	 */
	self.getCurrentTrack = function(success, error) {
		console.log('car.mediaplayer/client.js::getCurrentTrack');
		window.webworks.exec(success, error, _ID, 'getCurrentTrack', null, false);
	};
	
	/**
	 * Retrieves metadata for the specified media
	 * @param {Number} mediaSourceId The ID of the node's media source.
	 * @param {String} mediaNodeId The media node ID of which to retrieve metadata.
	 * @param {Function} success The success callback handler.
	 * @param {Function} [error] The error callback handler.
	 * @memberOf module:car.mediaplayer.Mediaplayer	 
	 * @method getMetadata
	 * @example
	 *
	 * function successCallback(metadata) {
	 * 	console.log("metadata title = " + metadata.title + "\n" +
	 * 				"metadata duration = " + metadata.duration + "\n" +
	 * 				"metadata artwork = " + metadata.artwork + "\n" +
	 * 				"metadata artist = " + metadata.artist + "\n" +
	 * 				"metadata album = " + metadata.album + "\n" +
	 * 				"metadata genre = " + metadata.genre + "\n" +
	 * 				"metadata disc = " + metadata.disc + "\n" +
	 * 				"metadata track = " + metadata.track + "\n" +
	 * 				"metadata width = " + metadata.width + "\n" +
	 * 				"metadata height = " + metadata.height);
	 * }
	 * 
	 * function errorCallback(error) {
	 * 	console.log(error.code, error.msg);
	 * }
	 * 
	 * // Instantiate a media player object, specifying the player name
	 * var mediaPlayer = new car.mediaplayer.MediaPlayer('playerName');
	 * 
	 * // Get metadata for the specified node
	 * mediaPlayer.getMetadata(0, '/songs/1', successCallback, errorCallback);
	 *
	 *
	 *
	 * @example REST
	 *
	 * Request:
	 * http://<car-ip>/car.mediaplayer/getMetadata?mediaSourceId=0&mediaNodeId=/songs/1
	 *
	 * Success Response:
	 * {
	 * 	"code": 1,
	 * 	"data": {
	 * 		"title": "Bo Bo's Groove",
	 * 		"duration": 318411,
	 * 		"artwork": "file:///apps/mediasources/imagecache//mme/2/original",
	 * 		"artist": "tomprincipato",
	 * 		"album": "Raising The Roof!",
	 * 		"genre": "rock",
	 * 		"disc": 0,
	 * 		"track": 0,
	 * 		"width": -1,
	 * 		"height": -1
	 * 	}
	 * }
	 *
	 * Error Response:
	 * {
	 *		code: -1,
	 *		msg: "An error has occurred"
	 * }
	 */
	self.getMetadata = function(mediaSourceId, mediaNodeId, success, error) {
		console.log('car.mediaplayer/client.js::getMetadata', mediaSourceId, mediaNodeId);
		var args = {
			mediaSourceId: mediaSourceId,
			mediaNodeId: mediaNodeId
		};
		window.webworks.exec(success, error, _ID, 'getMetadata', args, false);
	};
	
	/**
	 * Returns the state of the media player
	 * @param {Function} success The success callback handler.
	 * @param {Function} [error] The error callback handler.
	 * @memberOf module:car.mediaplayer.Mediaplayer	 
	 * @method getPlayerState
	 * @example
	 *
	 * function successCallback(playerState) {
	 * 	console.log("player state shuffle mode = " + playerState.shuffleMode + "\n" +
	 * 				"player state repeat mode = " + playerState.repeatMode + "\n" +
	 * 				"player state player status = " + playerState.playerStatus + "\n" +
	 * 				"player state playback rate = " + playerState.playbackRate);
	 * }
	 * 
	 * function errorCallback(error) {
	 * 	console.log(error.code, error.msg);
	 * }
	 * 
	 * // Instantiate a media player object, specifying the player name
	 * var mediaPlayer = new car.mediaplayer.MediaPlayer('playerName');
	 * 
	 * // Get the current player state
	 * mediaPlayer.getPlayerState(successCallback, errorCallback);
	 *
	 *
	 *
	 * @example REST
	 *
	 * Request:
	 * http://<car-ip>/car.mediaplayer/getPlayerState
	 *
	 * Success Response:
	 * {
	 * 	"code": 1,
	 * 	"data": {
	 * 		"shuffleMode": 0,
	 * 		"repeatMode": 0,
	 * 		"playerStatus": 2,
	 * 		"playbackRate": 1
	 * 	}
	 * }
	 *
	 * Error Response:
	 * {
	 *		code: -1,
	 *		msg: "An error has occurred"
	 * }
	 */
	self.getPlayerState = function(success, error) {
		console.log('car.mediaplayer/client.js::getPlayerState');
		window.webworks.exec(success, error, _ID, 'getPlayerState', null, false);
	};
	
	/**
	 * Start or resume playback of the current track session
	 * @memberOf module:car.mediaplayer.Mediaplayer
	 * @method play	 
	 * @example
	 *
	 * // Instantiate a media player object, specifying the player name
	 * var mediaPlayer = new car.mediaplayer.MediaPlayer('playerName');
	 * 
	 * // Play
	 * mediaPlayer.play();
	 *
	 *
	 *
	 * @example REST
	 *
	 * Request:
	 * http://<car-ip>/car.mediaplayer/play
	 *
	 * Success Response:
	 * {
	 * 	"code": 1
	 * }
	 *
	 * Error Response:
	 * {
	 *		code: -1,
	 *		msg: "An error has occurred"
	 * }
	 */
	self.play = function() {
		console.log('car.mediaplayer/client.js::play');
		window.webworks.execAsync(_ID, 'play');
	};
	
	/**
	 * Pause playback
	 * @memberOf module:car.mediaplayer.Mediaplayer
	 * @method pause	 
	 * @example
	 *
	 * // Instantiate a media player object, specifying the player name
	 * var mediaPlayer = new car.mediaplayer.MediaPlayer('playerName');
	 * 
	 * // Pause
	 * mediaPlayer.pause();
	 *
	 *
	 *
	 * @example REST
	 *
	 * Request:
	 * http://<car-ip>/car.mediaplayer/pause
	 *
	 * Success Response:
	 * {
	 * 	"code": 1
	 * }
	 *
	 * Error Response:
	 * {
	 *		code: -1,
	 *		msg: "An error has occurred"
	 * }
	 */
	self.pause = function() {
		console.log('car.mediaplayer/client.js::pause');
		window.webworks.execAsync(_ID, 'pause');
	};

	/**
	 * Stop playback
	 * @memberOf module:car.mediaplayer.MediaPlayer
	 * @method stop	 
	 * @example
	 *
	 * // Instantiate a media player object, specifying the player name
	 * var mediaPlayer = new car.mediaplayer.MediaPlayer('playerName');
	 * 
	 * // Stop
	 * mediaPlayer.stop();
	 *
	 *
	 *
	 * @example REST
	 *
	 * Request:
	 * http://<car-ip>/car.mediaplayer/stop
	 *
	 * Success Response:
	 * {
	 * 	"code": 1
	 * }
	 *
	 * Error Response:
	 * {
	 *		code: -1,
	 *		msg: "An error has occurred"
	 * }
	 */
	self.stop = function() {
		console.log('car.mediaplayer/client.js::stop');
		window.webworks.execAsync(_ID, 'stop');
	};
	
	/**
	 * Skip to the next track in the active track session
	 * @memberOf module:car.mediaplayer.Mediaplayer
	 * @method next	 
	 * @example
	 *
	 * // Instantiate a media player object, specifying the player name
	 * var mediaPlayer = new car.mediaplayer.MediaPlayer('playerName');
	 * 
	 * // Skip to the next track
	 * mediaPlayer.next();
	 *
	 *
	 *
	 * @example REST
	 *
	 * Request:
	 * http://<car-ip>/car.mediaplayer/next
	 *
	 * Success Response:
	 * {
	 * 	"code": 1
	 * }
	 *
	 * Error Response:
	 * {
	 *		code: -1,
	 *		msg: "An error has occurred"
	 * }
	 */
	self.next = function() {
		console.log('car.mediaplayer/client.js::next');
		window.webworks.execAsync(_ID, 'next');
	};
	
	/**
	 * Skip to the previous track in the active track session
	 * @memberOf module:car.mediaplayer.Mediaplayer
	 * @method previous	 
	 * @example
	 *
	 * // Instantiate a media player object, specifying the player name
	 * var mediaPlayer = new car.mediaplayer.MediaPlayer('playerName');
	 * 
	 * // Skip to the previous track
	 * mediaPlayer.previous();
	 *
	 *
	 *
	 * @example REST
	 *
	 * Request:
	 * http://<car-ip>/car.mediaplayer/previous
	 *
	 * Success Response:
	 * {
	 * 	"code": 1
	 * }
	 *
	 * Error Response:
	 * {
	 *		code: -1,
	 *		msg: "An error has occurred"
	 * }
	 */
	self.previous = function() {
		console.log('car.mediaplayer/client.js::previous');
		window.webworks.execAsync(_ID, 'previous');
	};
	
	/**
	 * Jumps to the specified index in the current track session.
	 * @param {Number} index The index of the track within the current track session.
	 * @memberOf module:car.mediaplayer.MediaPlayer
	 * @method jump	 
	 * @example
	 *
	 * // Instantiate a media player object, specifying the player name
	 * var mediaPlayer = new car.mediaplayer.MediaPlayer('playerName');
	 * 
	 * // Jump to another track in the active track session
	 * mediaPlayer.jump(4);
	 *
	 *
	 *
	 * @example REST
	 *
	 * Request:
	 * http://<car-ip>/car.mediaplayer/jump?index=4
	 *
	 * Success Response:
	 * {
	 * 	"code": 1
	 * }
	 *
	 * Error Response:
	 * {
	 *		code: -1,
	 *		msg: "An error has occurred"
	 * }
	 */
	self.jump = function(index) {
		console.log('car.mediaplayer/client.js::jump', index);
		window.webworks.execAsync(_ID, 'jump', { index: index });
	};
	
	/**
	 * Seek to a specific position in the current track
	 * @param {Number} position The track position, in ms.
	 * @memberOf module:car.mediaplayer.Mediaplayer
	 * @method seek	 
	 * @example
	 *
	 * // Instantiate a media player object, specifying the player name
	 * var mediaPlayer = new car.mediaplayer.MediaPlayer('playerName');
	 * 
	 * // Seek
	 * mediaPlayer.seek(20000);
	 *
	 *
	 *
	 * @example REST
	 *
	 * Request:
	 * http://<car-ip>/car.mediaplayer/seek?position=20000
	 *
	 * Success Response:
	 * {
	 * 	"code": 1
	 * }
	 *
	 * Error Response:
	 * {
	 *		code: -1,
	 *		msg: "An error has occurred"
	 * }
	 */
	self.seek = function(position) {
		console.log('car.mediaplayer/client.js::seek', position);
		window.webworks.execAsync(_ID, 'seek', { position: position });
	};
	
	/**
	 * Set the playback rate of the media player
	 * @param {Number} playbackRate The playback rate. A rate of 1.0 is regular play speed. Negative numbers result in
	 * reverse playback.
	 * @memberOf module:car.mediaplayer.Mediaplayer
	 * @method setPlaybackRate	 
	 * @example
	 *
	 * // Instantiate a media player object, specifying the player name
	 * var mediaPlayer = new car.mediaplayer.MediaPlayer('playerName');
	 * 
	 * // Fast forward 1.5x
	 * mediaPlayer.setPlaybackRate(1.5);
	 *
	 *
	 *
	 * @example REST
	 *
	 * Request:
	 * http://<car-ip>/car.mediaplayer/setPlaybackRate?playbackRate=1.5
	 *
	 * Success Response:
	 * {
	 * 	"code": 1
	 * }
	 *
	 * Error Response:
	 * {
	 *		code: -1,
	 *		msg: "An error has occurred"
	 * }
	 */
	self.setPlaybackRate = function(playbackRate) {
		console.log('car.mediaplayer/client.js::setPlaybackRate', playbackRate);
		window.webworks.execAsync(_ID, 'setPlaybackRate', { playbackRate: playbackRate });
	};
	
	/**
	 * Set the shuffle mode for the active track session.
	 * @param {MediaPlayer.ShuffleMode} shuffleMode The shuffle mode as per the MediaPlayer.ShuffleMode enumeration.
	 * @memberOf module:car.mediaplayer.Mediaplayer
	 * @method shuffle	 
	 * @example
	 *
	 * // Instantiate a media player object, specifying the player name
	 * var mediaPlayer = new car.mediaplayer.MediaPlayer('playerName');
	 * 
	 * // Turn shuffle mode on
	 * mediaPlayer.shuffle(car.mediaplayer.ShuffleMode.SHUFFLE_ON);
	 *
	 *
	 *
	 * @example REST
	 *
	 * Request:
	 * http://<car-ip>/car.mediaplayer/shuffle?shuffleMode=1
	 *
	 * Success Response:
	 * {
	 * 	"code": 1
	 * }
	 *
	 * Error Response:
	 * {
	 *		code: -1,
	 *		msg: "An error has occurred"
	 * }
	 */
	self.shuffle = function(shuffleMode) {
		console.log('car.mediaplayer/client.js::shuffle', shuffleMode);
		window.webworks.execAsync(_ID, 'shuffle', { shuffleMode: shuffleMode });
	};
	
	/**
	 * Set the repeat mode for the active track session
	 * @param {MediaPlayer.RepeatMode} repeatMode The repeat mode as per the MediaPlayer.RepeatMode enumeration.
	 * @memberOf module:car.mediaplayer.Mediaplayer
	 * @method repeat	 
	 * @example
	 *
	 * // Instantiate a media player object, specifying the player name
	 * var mediaPlayer = new car.mediaplayer.MediaPlayer('playerName');
	 * 
	 * // Turn repeat one mode on
	 * mediaPlayer.repeat(car.mediaplayer.RepeatMode.REPEAT_ONE);
	 *
	 *
	 *
	 * @example REST
	 *
	 * Request:
	 * http://<car-ip>/car.mediaplayer/repeat?repeatMode=2
	 *
	 * Success Response:
	 * {
	 * 	"code": 1
	 * }
	 *
	 * Error Response:
	 * {
	 *		code: -1,
	 *		msg: "An error has occurred"
	 * }
	 */
	self.repeat = function(repeatMode) {
		console.log('car.mediaplayer/client.js::repeat', repeatMode);
		window.webworks.execAsync(_ID, 'repeat', { repeatMode: repeatMode });
	};

	/**
	 * Watch for changes to media sources
	 * @param {Function} callback The function to be called when a change is detected.
	 * @return {Number} The watch ID.
	 * @memberOf module:car.mediaplayer.Mediaplayer
	 * @method watchMediaSource	 
	 * @example
	 * 
	 * // Define a callback function
	 * function myCallback(event) {
	 * }
	 * 
	 * // Instantiate a media player object, specifying the name of the player of which to watch
	 * var mediaPlayer = new car.mediaplayer.MediaPlayer('playerName');
	 * 
	 * // Add the watch
	 * var watchId = mediaPlayer.watchMediaSource(myCallback);
	 */
	self.watchMediaSource = function(callback) {
		console.log('car.mediaplayer/client.js::watchMediaSource');
		return _callback.watch(Event.MEDIA_SOURCE_CHANGE, callback);
	};
	
	/**
	 * Watch for changes to track sessions
	 * @param {Function} callback The function to be called when a change is detected.
	 * @return {Number} The watch ID.
	 * @memberOf module:car.mediaplayer.Mediaplayer
	 * @method watchTrackSession 	 
	 * @example
	 * 
	 * // Define a callback function
	 * function myCallback(event) {
	 * }
	 * 
	 * // Instantiate a media player object, specifying the name of the player of which to watch
	 * var mediaPlayer = new car.mediaplayer.MediaPlayer('playerName');
	 * 
	 * // Add the watch
	 * var watchId = mediaPlayer.watchTrackSession(myCallback);
	 */
	self.watchTrackSession = function(callback) {
		console.log('car.mediaplayer/client.js::watchTrackSession');
		return _callback.watch(Event.TRACK_SESSION_CHANGE, callback);
	};
	
	/**
	 * Watch for changes to the media player state
	 * @param {Function} callback The function to be called when a change is detected.
	 * @memberOf module:car.mediaplayer.Mediaplayer
	 * @method watchPlayerState	 
	 * @return {Number} The watch ID.
	 * @example
	 * 
	 * // Define a callback function
	 * function myCallback(event) {
	 * }
	 * 
	 * // Instantiate a media player object, specifying the name of the player of which to watch
	 * var mediaPlayer = new car.mediaplayer.MediaPlayer('playerName');
	 * 
	 * // Add the watch
	 * var watchId = mediaPlayer.watchPlayerState(myCallback);
	 */
	self.watchPlayerState = function(callback) {
		console.log('car.mediaplayer/client.js::watchPlayerState');
		return _callback.watch(Event.PLAYER_STATE_CHANGE, callback);
	};
	
	/**
	 * Watch for changes to the current track
	 * @param {Function} callback The function to be called when a change is detected.
	 * @memberOf module:car.mediaplayer.Mediaplayer
	 * @method watchTrack	 
	 * @return {Number} The watch ID.
	 * @example
	 * 
	 * // Define a callback function
	 * function myCallback(event) {
	 * }
	 * 
	 * // Instantiate a media player object, specifying the name of the player of which to watch
	 * var mediaPlayer = new car.mediaplayer.MediaPlayer('playerName');
	 * 
	 * // Add the watch
	 * var watchId = mediaPlayer.watchTrack(myCallback);
	 */
	self.watchTrack = function(callback) {
		console.log('car.mediaplayer/client.js::watchTrack');
		return _callback.watch(Event.TRACK_CHANGE, callback);
	};
	
	/**
	 * Watch for changes to the current track's position
	 * @param {Function} callback The function to be called when a change is detected.
	 * @memberOf module:car.mediaplayer.Mediaplayer
	 * @method watchTrackPosition	 
	 * @return {Number} The watch ID.
	 * @example
	 * 
	 * // Define a callback function
	 * function myCallback(event) {
	 * }
	 * 
	 * // Instantiate a media player object, specifying the name of the player of which to watch
	 * var mediaPlayer = new car.mediaplayer.MediaPlayer('playerName');
	 * 
	 * // Add the watch
	 * var watchId = mediaPlayer.watchTrackPosition(myCallback);
	 */
	self.watchTrackPosition = function(callback) {
		console.log('car.mediaplayer/client.js::watchTrackPosition');
		return _callback.watch(Event.TRACK_POSITION_CHANGE, callback);
	};
	
	/**
	 * Removes a watch callback
	 * @param {Number} watchId The watch ID.
	 * @memberOf module:car.mediaplayer.Mediaplayer
	 * @method cancelWatch	 
	 * @example
	 * 
	 * // Instantiate a media player object, specifying the name of the player of which to watch
	 * var mediaPlayer = new car.mediaplayer.MediaPlayer('playerName');
	 * 
	 * // Add a watch
	 * var watchId = mediaPlayer.watchMediaSource(function() {});
	 * 
	 * // Cancel the watch
	 * mediaPlayer.cancelWatch(watchId);
	 */
	self.cancelWatch = function(watchId) {
		console.log('car.mediaplayer/client.js::cancelWatch', watchId);
		_callback.cancelWatch(watchId);
	};
	
	// Initialize the media player
	if(!open(playerName)) {
		throw Error('Unable to open MediaPlayer with name "' + playerName + '"');
	}
	
	// Return the instance
	return this;
};

/*
 * Exports are the publicly accessible functions
 */
module.exports = {
		MediaPlayer: MediaPlayer,
		
		// Enumeration of media node types.
		MediaNodeType: MediaNodeType,
		
		// Enumeration of media player statuses.
		PlayerStatus: PlayerStatus,
		
		// Enumeration of media source change event types.
		MediaSourceEvent: MediaSourceEvent,
		
		// Enumeration of media source types.
		MediaSourceType: MediaSourceType,
		
		// Enumeration of track session change event types.
		TrackSessionEvent: TrackSessionEvent,
		
		//  Enumeration of repeat modes.
		RepeatMode: RepeatMode,
		
		//  Enumeration of shuffle modes.
		ShuffleMode: ShuffleMode
};