Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
if (window.ninemsn.portal.common.video.jqueryNoConflict) {
window.ninemsn.portal.common.video.$ = window.jQuery.noConflict(true);
}
(function ($, ninemsn) {
var video = ninemsn.portal.common.video; // used as a short-cut to the video namespace
video.enumerations = video.enumerations || {
account: {
ninemsn: "ninemsn",
ninemsnCatchup: "ninemsnCatchup",
msnNZ: "msnNZ",
thirdParty: "thirdParty"
}
};
video.enumerations.data = video.enumerations.data || {
adapter: {
msn: "msn",
Brightcove: "brightcove",
CCast: "ccast"
},
method: {
ID: "id",
IDs: "ids",
Tags: "tags",
Search: "search",
Related: "related"
},
searchOperator: {
All: "ALL",
Any: "ANY",
Exact: "EXACT"
}
};
video.enumerations.event = video.enumerations.event || {
player: {
loading: "player:loading",
loaded: "player:loaded",
error: "player:error"
},
video: {
loading: "video:loading",
loaded: "video:loaded",
opening: "video:opening",
buffering: "video:buffering",
playing: "video:playing",
paused: "video:paused",
stopped: "video:stopped",
playCompleted: "video:playCompleted",
changed: "video:changed",
error: "video:error"
},
ad: {
opening: "ad:opening",
playing: "ad:playing",
paused: "ad:paused",
playCompleted: "ad:playCompleted",
error: "ad:error"
}
};
video.enumerations.player = video.enumerations.player || {
adapter: {
msn: "msn",
Brightcove: "brightcove",
ads: {
freewheel: "freewheel"
}
},
type: {
Flash: "flash",
Silverlight: "silverlight",
HTML5: "html5"
}
};
video.enumerations.widget = video.enumerations.widget || {
type: {
leadWithImage: "leadWithImage",
list: "list",
playlist: "playlist",
suggestedVideos: "suggestedVideos"
}
};
video.Data = video.Data || function (dataConfig) {
/// <summary>Creates a new ninemsn.portal.common.video.Data object</summary>
/// <param name="config" optional="true" type="Object"></param>
/// <returns type="ninemsn.portal.common.video.Data" />
// Ensure we are working with an instance of the object
var _context = (this instanceof video.Data) ? this : new video.Data(dataConfig);
// stores the data adapter object
var _adapter;
// stores the config object
var _config = {
// default settings
account: video.enumerations.account.ninemsn,
adapter: video.enumerations.data.adapter.msn,
paging: {
page: 1,
size: 10
},
cache: true
};
_context.dispose = function () {
/// <summary>
/// In addition to the element itself, all bound events and data associated with the element is removed.
/// </summary>
_context.logger.debug("is being disposed of");
if (_adapter.dispose != null) {
_adapter.dispose();
}
_adapter = null;
_config = null;
_context = null;
};
(function () {
// Apply brightcove adapter if account = ninemsnCatchup
if (dataConfig.hasOwnProperty("account") && dataConfig["account"] == video.enumerations.account.ninemsnCatchup) {
_config.adapter = video.enumerations.data.adapter.Brightcove;
}
// Merge the default settings with the supplied configuration
_config = $.extend(true, _config, dataConfig); // the configuration of the data object
// Create a new config getter
_context.config = new video.utils.Config(_context, _config);
// Attach the logger object
_context.logger = new video.utils.Logger(
$.extend(
true,
{
name: "ninemsn.portal.common.video.Data"
},
_context.config("logger")
)
);
// init the data adapter
if (_dataAdapters.hasOwnProperty(_config["adapter"])) {
_adapter = new _dataAdapters[_config["adapter"]](_context);
} else {
_context.logger.error("'" + _config["adapter"] + "' is an unknown Data adapter");
}
})();
};
var _dataAdapters = _dataAdapters || {
/// <summary>Namespace for ninemsn video data adapters (MSN, Service Framework, etc.)</summary>
};
_dataAdapters[video.enumerations.data.adapter.msn] = _dataAdapters[video.enumerations.data.adapter.msn] || function (dataContext) {
var _context = dataContext;
var _config = $.extend(
true,
{
// default settings
baseUrl: "http://edge1.catalog.video.msn.com/",
fileFilter: 80
},
_context.config("msn")
); // the configuration of the player
var _cacheKey = "{market}_{method}_{params}_{page}_{pageSize}_{sortField}_{sortDirection}_{fileFilter}";
var _jqxhr;
var _methods = {};
var _translations = {
market: {
// Market (ninemsn > msn)
ninemsn: "en-au", ninemsnCatchup: "alt2-en-au", msnNZ: "en-nz", thirdParty: "alt-en-au"
},
sort_by: {
// Sort By (ninemsn > msn)
ID: "VideoID", START_DATE: "ActiveStartDate"
},
sort_order: {
// Sort Order (ninemsn > msn)
ASC: 1, DESC: -1
},
search_operator: {
// Search Operator (ninemsn > msn)
ALL: 0, ANY: 1, EXACT: 2
}
};
var normalise = function (data, callback) {
if (data != null) {
var response = {
pageNumber: (function () { return data.hasOwnProperty("$ind") ? parseInt(data.$ind) : null; })(),
pageSize: (function () { return data.hasOwnProperty("$ps") ? parseInt(data.$ps) : null; })(),
totalVideos: (function () { return data.hasOwnProperty("$total") ? parseInt(data.$total) : null; })(),
videos: []
};
if (response.pageSize != null && response.pageNumber != null) {
// correct the page number
response.pageNumber = response.pageNumber - 1;
response.pageNumber = response.pageNumber + response.pageSize;
response.pageNumber = response.pageNumber / response.pageSize;
}
if (data.hasOwnProperty("video")) {
// multiple videos in the response
for (var videoItem in data.video) {
videoItem = data.video[videoItem];
if (typeof (videoItem) == "object") {
response.videos.push(
createVideoObject(videoItem)
);
}
}
} else {
// single video in the response (always return an array)
response.videos = [createVideoObject(data)];
}
// Return the normalised data
callback(response);
} else {
_context.logger.warn("No data in the response");
}
};
var createVideoObject = function (data) {
var videoObject = {
id: data.uuid.$,
title: data.title.$,
description: data.description.$,
duration: data.durationSecs.$,
publishDate: video.utils.createUtcDateFromString(data.startDate.$),
source: data.source.$friendlyName,
reporting: {
plays: { total: -1 }
},
account: video.utils.msn.getAccountFromMarket(data.tags.tag[0].$market),
rawObject: data
};
// Get the image
videoObject.image = function (width, height) {
return "http://img2.catalog.video.msn.com/image.aspx?uuid=" + videoObject.id + "&w=" + width + "&h=" + height;
};
// Get the reporting stats
if (data.hasOwnProperty("usage") && data.usage.hasOwnProperty("usageItem")) {
for (var i = 0; i < data.usage.usageItem.length; i++) {
var item = data.usage.usageItem[i];
// Plays
if (parseInt(item.$counterType) == 1) {
videoObject.reporting.plays.total = parseInt(item.$totalCount);
}
}
}
return videoObject;
};
/*
MSN Caches the data on distinct url's which means
that if we have a random callbackName the data is never cached.
This cache key is used to provide a consistent callbackName
*/
var buildCacheKey = function (key, value) {
if (_cacheKey.indexOf(key) > -1) {
_cacheKey = _cacheKey.replace(key, value);
}
};
var getCleanCacheKey = function () {
// cleanup the cache key
_cacheKey = _cacheKey.replace(/[^A-Za-z0-9_]/ig, "");
if (typeof window[_cacheKey] == "function") {
_context.logger.debug("Cache Key in already in use", _cacheKey);
_cacheKey = _cacheKey + "_rand" + (Math.floor(Math.random() * 1100));
}
_context.logger.debug("Cache Key", _cacheKey);
return _cacheKey;
};
var request = function (query) {
query = _config.baseUrl + query;
if (_context.config("paging") != null) {
// Page size
var pageSize = _context.config("paging.size");
if (pageSize != null) {
query += "&ps=" + pageSize;
buildCacheKey("{pageSize}", pageSize);
// Page number
var pageNumber = _context.config("paging.page");
if (pageNumber != null) {
var indent = (pageNumber * pageSize);
indent = (indent - pageSize);
indent++;
query += "&ind=" + indent;
buildCacheKey("{page}", indent);
}
}
}
/*
File filter:
Allows you to filter your return results based on pre-defined filters e.g. playable on mobile
*/
if (_config["fileFilter"] != null) {
query += "&ff=" + _config["fileFilter"];
buildCacheKey("{fileFilter}", _config["fileFilter"]);
}
// Sorting
if (_context.config("sort") != null) {
var sort = _context.config("sort");
var fields = "";
var directions = "";
var translate = function (item) {
// Translate the field
item.by = (_translations.sort_by.hasOwnProperty(item.by) ? _translations.sort_by[item.by] : item.by);
fields += (fields == "") ? item.by : "," + item.by;
// Translate the direction
item.order = (_translations.sort_order.hasOwnProperty(item.order) ? _translations.sort_order[item.order] : item.order);
directions += (directions == "") ? item.order : "," + item.order;
};
if (sort instanceof Array) {
for (var i = 0; i < sort.length; i++) {
translate(sort[i]);
}
} else {
translate(sort);
}
if (fields != "") {
query += "&sf=" + fields;
buildCacheKey("{sortField}", fields);
}
if (directions != "") {
query += "&sd=" + directions;
buildCacheKey("{sortDirection}", directions);
}
}
query += "&responseEncoding=json&callbackName=?";
_context.logger.log("Request", query);
// Execute the query against the MSN catalogue
_jqxhr = $.ajax({
url: query,
dataType: "jsonp",
cache: _context.config("cache"),
jsonpCallback: getCleanCacheKey(),
success: (function (data) {
_context.logger.log("Request Complete", data);
normalise(data, _context.config("success"));
}),
error: (
// Supported as of jQuery 1.5
function (msg) {
_context.logger.error("Data request failed - " + msg.status, msg);
// If a custom error handler has been assigned, raise it
if (_context.config("error") != null) {
_context.config("error")(msg);
}
}
)
});
};
_methods[video.enumerations.data.method.ID] = function () {
buildCacheKey("{params}", _context.config("filter.id"));
request("videobyuuid.aspx?uuid=" + _context.config("filter.id"));
};
_methods[video.enumerations.data.method.IDs] = function () {
var ids = "";
var uuids = _context.config("filter.ids");
for (var id in uuids) {
ids += (ids == "") ? uuids[id] : "," + uuids[id];
}
buildCacheKey("{params}", ids);
request("videobyuuids.aspx?uuids=" + ids);
};
_methods[video.enumerations.data.method.Tags] = function () {
var namespaces = "";
var values = "";
var tags = _context.config("filter.tags");
for (var i = 0; i < tags.length; i++) {
var tag = tags[i];
if (!(tag.value instanceof Array)) {
tag.value = new Array(tag.value);
}
for (var t = 0; t < tag.value.length; t++) {
namespaces += (namespaces == "") ? tag.name : "," + tag.name;
values += (values == "") ? tag.value[t] : "," + tag.value[t];
}
}
buildCacheKey("{params}", namespaces + "_" + values);
request("videobytag.aspx?mk=" + _config.market + "&ns=" + namespaces + "&tag=" + values);
};
_methods[video.enumerations.data.method.Search] = function () {
var term = _context.config("filter.term");
var termResult = "";
var operator = _context.config("filter.operator");
var i;
if (term instanceof Array) {
for (i = 0; i < term.length; i++) {
termResult += (termResult == "") ? term[i] : " " + term[i];
}
} else {
termResult = term;
}
// handle tags in search
var namespaces = "";
var values = "";
var tags = _context.config("filter.tags");
if (tags != null) {
for (i = 0; i < tags.length; i++) {
var tag = tags[i];
if (!(tag.value instanceof Array)) {
tag.value = new Array(tag.value);
}
for (var t = 0; t < tag.value.length; t++) {
namespaces += (namespaces == "") ? tag.name : "," + tag.name;
values += (values == "") ? tag.value[t] : "," + tag.value[t];
}
}
}
var tagQuery = namespaces.length > 0 ? "&ns=" + namespaces + "&tag=" + values : "";
// Translate the operator
operator = (_translations.search_operator.hasOwnProperty(operator) ? _translations.search_operator[operator] : "0");
buildCacheKey("{params}", term + "_" + namespaces + "_" + values + "_" + operator);
request("search.aspx?mk=" + _config.market + "&q=" + encodeURIComponent(termResult) + "&op=" + operator + tagQuery);
};
_methods[video.enumerations.data.method.Related] = function () {
buildCacheKey("{params}", _context.config("filter.id"));
request("relatedvideos.aspx?mk=" + _config.market + "&uuids=" + _context.config("filter.id"));
};
this.dispose = function () {
_context.logger.log("Closing any active MSN data requests");
if (_jqxhr != null) {
_jqxhr.abort();
}
};
(function () {
if (_context.config("account") != null) {
// Set the account
if (_translations.market.hasOwnProperty(_context.config("account"))) {
_config["market"] = _translations.market[_context.config("account")];
buildCacheKey("{market}", _config["market"]);
} else {
_context.logger.error("The supplied account is not supported on this adapter", _context.config("account"));
}
} else {
_context.logger.error("Account has not been set on the adapter");
}
// Execute the method
if (_methods.hasOwnProperty(_context.config("method"))) {
buildCacheKey("{method}", _context.config("method"));
_methods[_context.config("method")]();
} else {
_context.logger.error("Data method not implemented", _context.config("data"));
}
})();
};
_dataAdapters[video.enumerations.data.adapter.Brightcove] = _dataAdapters[video.enumerations.data.adapter.Brightcove] || function (dataContext) {
var _context = dataContext;
var _config = $.extend(
true,
{
// default settings
baseUrl: "http://api.brightcove.com/services/library?command=",
videoFields: "&video_fields=id,referenceId,version,name,shortDescription,publishedDate,startDate,endDate,length,itemState,thumbnailURL,videoStillURL,playsTotal",
customFields: "&custom_fields=genre,network,provider,series,season,episode,originalairdate,classification",
item_count: "&get_item_count=true"
},
_context.config("brightcove")
); // the configuration of the player
var _jqxhr;
var _methods = {};
var _translations = {
market: {
// Market (ninemsn > msn)
ninemsn: "en-au", ninemsnCatchup: "alt2-en-au", msnNZ: "en-nz", thirdParty: "alt-en-au"
},
sort_by: {
// Sort By (ninemsn > brightcove)
ActiveStartDate: "START_DATE", monthlyCount: "PLAYS_TOTAL"
},
search_operator: {
// Search Operator (ninemsn > msn)
ALL: "all", ANY: "any", NONE: "none", EXACT: "exact"
}
};
var normalise = function (data, callback) {
if (data != null) {
var response = {
pageNumber: (function () { return data.hasOwnProperty("page_number") ? parseInt(data.page_number) : null; })(),
pageSize: (function () { return data.hasOwnProperty("page_size") ? parseInt(data.page_size) : null; })(),
totalVideos: (function () { return data.hasOwnProperty("total_count") ? parseInt(data.total_count) : null; })(),
videos: []
};
if (response.pageSize != null && response.pageNumber != null) {
// correct the page number
response.pageNumber = response.pageNumber + 1;
}
if (data.hasOwnProperty("items")) {
// multiple videos in the response
for (var videoItem in data.items) {
videoItem = data.items[videoItem];
if (typeof (videoItem) == "object") {
response.videos.push(
createVideoObject(videoItem)
);
}
}
} else {
// single video in the response (always return an array)
if (data.hasOwnProperty("error")) {
_context.logger.error(data.error);
}
else {
response.videos = [createVideoObject(data)];
}
}
// Return the normalised data
callback(response);
} else {
_context.logger.warn("No data in the response");
}
};
var createVideoObject = function (data) {
var videoObject = {
id: data.id,
title: data.name,
description: data.shortDescription,
duration: data.length / 1000,
publishDate: data.startDate != null ? new Date(parseInt(data.startDate)) : new Date(parseInt(data.publishedDate)),
source: data && data.customFields && data.customFields.series,
reporting: {
plays: { total: data.playsTotal || 0 }
},
account: _context.config("account"),
rawObject: data
};
videoObject.image = function (width, height) {
return "http://images.ninemsn.com.au/resizer.aspx?width=" + width + "&height=" + height + "&url=" + data.videoStillURL;
};
return videoObject;
};
var request = function (query) {
query = _config.baseUrl + query;
if (_context.config("paging") != null) {
// Page size
var pageSize = _context.config("paging.size");
if (pageSize != null) {
query += "&page_size=" + pageSize;
// Page number
var pageNumber = _context.config("paging.page");
if (pageNumber != null) {
query += "&page_number=" + (pageNumber - 1);
}
}
}
// Sorting
if (_context.config("sort") != null) {
var sort = _context.config("sort");
var sortby = "";
var directions = "";
var translate = function (item) {
// Translate the sort field
item.by = (_translations.sort_by.hasOwnProperty(item.by) ? _translations.sort_by[item.by] : item.by);
sortby += (sortby == "") ? item.by + ":" + item.order : "," + item.by + ":" + item.order;
};
if (sort instanceof Array) {
for (var i = 0; i < sort.length; i++) {
translate(sort[i]);
}
} else {
translate(sort);
}
if (sortby != "") {
query += "&sort_by=" + sortby;
}
}
query += "&token=" + _config.token + _config.videoFields + _config.customFields + _config.item_count;
query += "&callback=?";
_context.logger.log("Request", query);
// Execute the query against the MSN catalogue
_jqxhr = $.ajax({
url: query,
dataType: "jsonp",
cache: _context.config("cache"),
success: (function (data) {
_context.logger.log("Request Complete", data);
normalise(data, _context.config("success"));
}),
error: (
// Supported as of jQuery 1.5
function (msg) {
_context.logger.error("Data request failed - " + msg.status, msg);
// If a custom error handler has been assigned, raise it
if (_context.config("error") != null) {
_context.config("error")(msg);
}
}
)
});
};
_methods[video.enumerations.data.method.ID] = function () {
request("find_video_by_id&video_id=" + _context.config("filter.id"));
};
_methods[video.enumerations.data.method.IDs] = function () {
var ids = "";
var uuids = _context.config("filter.ids");
for (var id in uuids) {
ids += (ids == "") ? uuids[id] : "," + uuids[id];
}
request("find_videos_by_ids&video_ids=" + ids);
};
_methods[video.enumerations.data.method.Tags] = function () {
var operator = _context.config("filter.operator");
var tagQuery = "";
var tags = _context.config("filter.tags");
// Translate the operator
operator = (_translations.search_operator.hasOwnProperty(operator) ? _translations.search_operator[operator] : "all");
// Add Exact Attribute
if (operator == _translations.search_operator.EXACT) {
tagQuery += "exact=true";
operator = _translations.search_operator.ALL;
}
for (var i = 0; i < tags.length; i++) {
var tag = tags[i];
if (!(tag.value instanceof Array)) {
tag.value = new Array(tag.value);
}
for (var t = 0; t < tag.value.length; t++) {
var oneTag = operator + "=" + tag.name + ":" + tag.value[t];
tagQuery += (tagQuery == "") ? oneTag : "&" + oneTag;
}
}
request("search_videos&" + tagQuery);
};
_methods[video.enumerations.data.method.Search] = function () {
var term = _context.config("filter.term");
var termResult = "";
var operator = _context.config("filter.operator");
var i;
// Translate the operator
operator = (_translations.search_operator.hasOwnProperty(operator) ? _translations.search_operator[operator] : "all");
if (term instanceof Array) {
for (i = 0; i < term.length; i++) {
var ontTerm = operator + "=" + term[i];
termResult += (termResult == "") ? ontTerm : "&" + ontTerm;
}
} else {
termResult = operator + "=" + term;
}
request("search_videos&" + termResult);
};
_methods[video.enumerations.data.method.Related] = function () {
request("find_related_videos&video_id=" + _context.config("filter.id"));
};
var setAccountOverrides = function () {
/// <summary>Updates the config object to include account specific overrides</summary>
if (_context.config("account") != null) {
var _accountOverrides = {};
// ninemsn catchup
_accountOverrides[video.enumerations.account.ninemsnCatchup] = function () {
_config["token"] = "ogxhPgSphIVa2hhxbi9oqtYwtg032io4B4-ImwddYliFWHqS0UfMEw..";
};
_accountOverrides[video.enumerations.account.ninemsn] = function () {
_config["token"] = "Vb3fqavTKFDDZbnnGGtbhKxam7uHduOnob-2MJlpHmUnzSMWbDe5bg..";
};
if (_accountOverrides.hasOwnProperty(_context.config("account"))) {
_accountOverrides[_context.config("account")]();
} else {
_context.logger.error("The supplied account is not supported on this adapter", _context.config("account"));
}
} else {
_context.logger.error("Account has not been set on the adapter");
}
};
this.dispose = function () {
_context.logger.log("Closing any active MSN data requests");
if (_jqxhr != null) {
_jqxhr.abort();
}
};
(function () {
// Set the account overrides
setAccountOverrides();
// Execute the method
if (_methods.hasOwnProperty(_context.config("method"))) {
_methods[_context.config("method")]();
} else {
_context.logger.error("Data method not implemented", _context.config("data"));
}
})();
};
_dataAdapters[video.enumerations.data.adapter.CCast] = _dataAdapters[video.enumerations.data.adapter.CCast] || function (dataContext) {
var _context = dataContext;
var _config = $.extend(
true,
{
// default settings
// CCast proxy base url, support jsonp Callback, callback parameter: ?&callback=
baseUrl: "http://soocache.elasticbeanstalk.com/CCastTimeline/GetVideosByEpisode"
},
_context.config("ccast")
); // the configuration of the data adapter
var _jqxhr;
var _methods = {};
var normalise = function (data, callback) {
if (data != null) {
var response = {
videos: []
};
var episodeId = data.episode.id;
if (data.hasOwnProperty("events")) {
var possibleImgFormats = data.defaultValues.possibleImageFormats;
// multiple videos in the response
for (var event in data.events) {
event = data.events[event];
if (event.evsContent && event.evsContent.detail && event.evsContent.detail.camList) {
for (var cam in event.evsContent.detail.camList) {
cam = event.evsContent.detail.camList[cam];
if (typeof (cam) == "object" && cam.available) {
response.videos.push(
createVideoObject(cam, event, possibleImgFormats, episodeId)
);
}
}
}
}
}
// Return the normalised data
callback(response);
} else {
_context.logger.warn("No data in the response");
}
};
var getImgFormatFromLookup = function (possibleImgFormats, lookupName) {
for (var imgFormat in possibleImgFormats) {
imgFormat = possibleImgFormats[imgFormat];
if (imgFormat.lookupName == lookupName) {
return imgFormat;
}
}
return null;
};
var createVideoObject = function (cam, event, possibleImgFormats, episodeId) {
var videoObject = {
id: cam.video.name,
title: event.evsContent.title,
description: event.evsContent.description,
//duration: null,
publishDate: new Date(event.tcInDate + " " + event.tcInTime.substring(0, 2) + ":" + event.tcInTime.substring(2, 4) +
":" + event.tcInTime.substring(4, 6) + ":" + event.tcInTime.substring(6, 8)),
source: episodeId,
tags: [{ name: 'eventId', value: event.id }, { name: 'camId', value: cam.id}]
/*
reporting: {
plays: { total: cam.video.ratings.total || 0 }
},
account: _context.config("account"),
rawObject: cam
*/
};
// Push event keywords
for (var keyword in event.evsContent.keywordList) {
keyword = event.evsContent.keywordList[keyword];
videoObject.tags.push({ name: keyword.type.toLowerCase(), value: keyword.value });
}
// Get largest possible image format
var largestImgFormat = { width: 0 };
for (var imgFormat in cam.video.videoThumb.availableImageFormats) {
imgFormat = cam.video.videoThumb.availableImageFormats[imgFormat];
imgFormat = getImgFormatFromLookup(possibleImgFormats, imgFormat.lookupName);
if (imgFormat.width > largestImgFormat.width) {
largestImgFormat = imgFormat;
}
}
videoObject.image = function (width, height) {
return "http://images.ninemsn.com.au/resizer.aspx?width=" + width + "&height=" + height + "&url=" +
largestImgFormat.deliveries[0].url + cam.video.name.substring(0, 3) + "/" + cam.video.name.substring(3, 6) +
"/" + cam.video.name.substring(6, 9) + "/" + cam.video.name + "_" + largestImgFormat.profile + "." +
largestImgFormat.extension;
};
return videoObject;
};
var request = function (query) {
query = _config.baseUrl + query;
query += "&callback=?";
_context.logger.log("Request", query);
// Execute the query against the CCast API catalogue cached Proxy
_jqxhr = $.ajax({
url: query,
dataType: "jsonp",
cache: _context.config("cache"),
success: (function (data) {
_context.logger.log("Request Complete", data);
normalise(data, _context.config("success"));
}),
error: (
// Supported as of jQuery 1.5
function (msg) {
_context.logger.error("Data request failed - " + msg.status, msg);
// If a custom error handler has been assigned, raise it
if (_context.config("error") != null) {
_context.config("error")(msg);
}
}
)
});
};
_methods[video.enumerations.data.method.Tags] = function () {
var appId = "", episodeId = "";
var tags = _context.config("filter.tags");
for (var i = 0; i < tags.length; i++) {
var tag = tags[i];
if (tag.name == 'applicationId') {
appId = tag.value;
}
if (tag.name == 'episodeId') {
episodeId = tag.value;
}
}
request("?applicationId=" + appId + "&episodeId=" + episodeId);
};
this.dispose = function () {
_context.logger.log("Closing any active data requests");
if (_jqxhr != null) {
_jqxhr.abort();
}
};
(function () {
// Set the account overrides
//setAccountOverrides();
// Execute the method
if (_methods.hasOwnProperty(_context.config("method"))) {
_methods[_context.config("method")]();
} else {
_context.logger.error("Data method not implemented", _context.config("data"));
}
})();
};
video.Player = video.Player || function (playerConfig) {
/// <summary>Creates a new ninemsn.portal.common.video.Player object</summary>
/// <param name="config" optional="true" type="Object"></param>
/// <returns type="ninemsn.portal.common.video.Player" />
// Ensure we are working with an instance of the object
var _context = (this instanceof video.Player) ? this : new video.Player(playerConfig);
// the player as a jQuery object, all events are attached to this
var _player;
// stores the player adapter object
var _adapter;
// stores the config object
var _config = {
// Account
account: video.enumerations.account.ninemsn,
// Adapter to use
adapter: video.enumerations.player.adapter.msn,
// Width/Height
width: 640,
height: 360
};
_context.on = function (event, handler) {
/// <summary>
/// Attach an event handler function for one or more events to the selected elements.
/// </summary>
/// <param name="event" type="String">One or more space-separated event types, such as "video:loaded" or "ad:paused".</param>
/// <param name="handler" type="Function">A function to execute when the event is triggered. The value false is also allowed as a shorthand for a function that simply does return false.</param>
_context.logger.debug("on", { event: event, handler: handler });
if (_player != null) {
// As of jQuery 1.7, the .on() method is the preferred method for attaching event handlers to a document
// If .on() is support use it, otherwise use the old .bind() method
if (typeof (_player.on) != "undefined") {
_player.on(event, handler);
} else {
_player.bind(event, handler);
}
} else {
_context.logger.error("The player object is undefined");
}
};
_context.one = function (event, handler) {
/// <summary>
/// Attach a handler to an event for the elements. The handler is executed at most once per element.
/// </summary>
/// <param name="event" type="String">One or more space-separated event types, such as "video:loaded" or "ad:paused".</param>
/// <param name="handler" type="Function">A function to execute when the event is triggered. The value false is also allowed as a shorthand for a function that simply does return false.</param>
_context.logger.debug("one", { event: event, handler: handler });
if (_player != null) {
// Executes once and then removes itself
_player.one(event, handler);
}
};
_context.off = function (event, handler) {
/// <summary>
/// Remove an event handler.
/// </summary>
/// <param name="event" type="String">One or more space-separated event types, such as "video:loaded" or "ad:paused".</param>
/// <param name="handler" type="Function">A handler function previously attached for the event(s), or the special value false.</param>
_context.logger.debug("off", { event: event, handler: handler });
if (_player != null) {
// As of jQuery 1.7, the .off() method is the preferred method for removing event handlers
// If .off() is support use it, otherwise use the old .unbind() method
if (typeof (_player.off) != "undefined") {
_player.off(event, handler);
} else {
_player.unbind(event, handler);
}
}
};
_context.trigger = function (event, data) {
/// <summary>
/// Execute all handlers and behaviours attached to the matched elements for the given event object.
/// </summary>
/// <param name="event" type="String">The event type, such as "video:loaded" or "ad:paused".</param>
/// <param name="data" optional="true" type="Object">Additional data passed to the event handler as additional arguments</param>
if (_player != null) {
_context.logger.log(event, data);
_player.trigger(event, data);
}
};
_context.send = function (type, value) {
/// <summary>
/// Sends a message to the inner object.
/// </summary>
/// <param name="type" type="String">The type of message to send, such as “PLAY”</param>
/// <param name="value" optional="true" type="Object">The value to send with the message</param>
_context.logger.log("send", { type: type, value: value });
if (_adapter != null) {
_adapter.send(type, value);
} else {
_context.logger.warn(_context.config("adapter") + " does not implement 'send'");
}
};
_context.get = function (property) {
/// <summary>
/// Get the value of the given property.
/// </summary>
/// <param name="property" type="String">The property to return, such as “VOLUME”</param>
_context.logger.log("get", property);
if (_adapter != null) {
return _adapter.get(property);
} else {
_context.logger.warn(_context.config("adapter") + " does not implement 'get'");
return null;
}
};
_context.dispose = function () {
/// <summary>
/// In addition to the element itself, all bound events and data associated with the element is removed.
/// </summary>
_context.logger.debug("is being disposed of");
if (_adapter != null && _adapter.dispose != null) {
_adapter.dispose();
}
_adapter = null;
_player.empty(); // Remove the player from the page
_player = null;
// Remove from the list of object
delete video.objects[_config["outputLocation"]];
_context = null;
};
(function () {
// Apply Brightcove adapter if account = ninemsnCatchup
if (playerConfig.hasOwnProperty("account") && playerConfig["account"] == video.enumerations.account.ninemsnCatchup) {
_config.adapter = video.enumerations.player.adapter.Brightcove;
}
// Merge the default settings with the supplied configuration
_config = $.extend(true, _config, playerConfig);
// Create a new config getter
_context.config = new video.utils.Config(_context, _config);
// Attach the logger object
_context.logger = new video.utils.Logger(
$.extend(
true,
{
name: (function () {
return "ninemsn.portal.common.video.Player | " + _context.config("outputLocation");
})()
},
_context.config("logger")
)
);
// Raise warning if user trying to turn off ADs
if (!!(_context.config("ads"))) {
if (_context.config("ads").hasOwnProperty("enabled") && _context.config("ads").enabled == false) {
_context.logger.warn("ads:{enabled:false} (Turn off AD) feature is no longer supported. ADs are managed at Freewheel level.");
}
}
// Get the player as a jQuery object
_player = $("#" + _context.config("outputLocation"));
// Create the player object using the specified adapter
if (_playerAdapters.hasOwnProperty(_config["adapter"])) {
// Apply get targeting string to longform
if (_config.adapter == video.enumerations.player.adapter.Brightcove) {
$.when(video.utils.GetTargetString()).then(
function (adObj) { // defered Success call back, override context config with Target String
_config = $.extend(true, _config, adObj);
_context.config = new video.utils.Config(_context, _config);
_adapter = new _playerAdapters[_config["adapter"]](_context);
},
function () { // Fail call back
_adapter = new _playerAdapters[_config["adapter"]](_context);
}
);
}
else {
_adapter = new _playerAdapters[_config["adapter"]](_context);
}
} else {
_context.logger.error("'" + _config["adapter"] + "' is an unknown Player adapter");
}
// Add the player to the list of video objects on the page
video.objects[_config["outputLocation"]] = _context;
})();
};
var _playerAdapters = _playerAdapters || {
/// <summary>Namespace for ninemsn video player adapters (MSN, Brightcove, YouTube, etc.)</summary>
};
_playerAdapters[video.enumerations.player.adapter.msn] = _playerAdapters[video.enumerations.player.adapter.msn] || function (playerContext) {
/// <summary>Creates a new ninemsn.portal.common.video.player.adapters.msn objec</summary>
/// <returns type="ninemsn.portal.common.video.player">A MSN video player object</returns>
// Get the player's context
var _context = playerContext;
// Is the player loaded?
var _loaded = false;
// There is no common way to get/send messages to a player
// These are the translations for the MSN video player
var _translations = {
market: {
// Market (ninemsn > msn)
ninemsn: "en-au", ninemsnCatchup: "alt2-en-au", msnNZ: "en-nz", thirdParty: "alt-en-au"
},
send: {
// Send (ninemsn > msn)
PLAY: "playVideo", PAUSE: "pauseVideo", STOP: "stopVideo", SEEK: "seekVideo",
VOLUME: "setVolume", MUTE: "setVolume", LOAD: "loadVideo"
},
get: {
// Get (ninemsn > msn)
VOLUME: "volume", MUTED: "volume", POSITION: "currentVideoPosition", DURATION: "currentVideoPosition",
STATUS: "playbackStatus", META: "currentVideo", FULLSCREEN: "isFullScreen"
},
event: {
// Events (msn > ninemsn)
adOpening: video.enumerations.event.ad.opening, adPlaying: video.enumerations.event.ad.playing, adPaused: video.enumerations.event.ad.paused,
adPlayFailed: video.enumerations.event.ad.error, adPlayCompleted: video.enumerations.event.ad.playCompleted,
loading: video.enumerations.event.video.loading, loaded: video.enumerations.event.video.loaded, ready: video.enumerations.event.video.loaded,
videoChanged: video.enumerations.event.video.changed, videoOpening: video.enumerations.event.video.opening, videoPlaying: video.enumerations.event.video.playing,
videoPaused: video.enumerations.event.video.paused, playbackStopped: video.enumerations.event.video.stopped, videoBuffering: video.enumerations.event.video.buffering,
playbackCompleted: video.enumerations.event.video.playCompleted, videoPlayFailed: video.enumerations.event.video.error
},
sort_by: {
// Sort By (ninemsn > msn)
ID: "VideoID", START_DATE: "Date"
},
sort_order: {
// Sort Order (ninemsn > msn)
ASC: "Ascending", DESC: "Descending"
}
};
// The ID of the player object
var _id;
/// The default configuration of the player extended by the supplied settings
var _config = $.extend(
true,
{
// default settings
overflow: "hidden",
defaultDomain: {
loadCss: "false"
},
playerOverrides: {
MsnPlayerBaseLinkOverride: (function () { return location.protocol + "//" + location.host + "/video/"; } ())
}
},
_context.config("msn")); // the configuration of the player
var _outputLocation; // The ID of the HTML element where the player is rendered
var _videoFilter = {};
var _videoLoaded = false;
_videoFilter[video.enumerations.data.method.ID] = function () {
return {
type: "Uuid",
uuids: [_context.config("data.filter.id")]
};
};
_videoFilter[video.enumerations.data.method.IDs] = function () {
return {
type: "Uuid",
uuids: _context.config("data.filter.ids")
};
};
_videoFilter[video.enumerations.data.method.Tags] = function () {
var tags = [];
for (var i = 0; i < _context.config("data.filter.tags").length; i++) {
var tag = _context.config("data.filter.tags")[i];
if (!(tag.value instanceof Array)) {
tag.value = new Array(tag.value);
}
for (var t = 0; t < tag.value.length; t++) {
tags.push({
"$namespace": tag.name,
"$": tag.value[t]
});
}
}
return {
type: "tag",
tags: tags
};
};
_videoFilter[video.enumerations.data.method.Search] = function () {
return {
type: "search",
search: _context.config("data.filter.term")
};
};
var setMarketOverrides = function () {
/// <summary>Updates the config object to include market specific overrides</summary>
if (_context.config("account") != null) {
var _marketOverrides = {};
// ninemsn
_marketOverrides[video.enumerations.account.ninemsn] = function () {
_config["configName"] = "NetworkVideoPlayerFreewheel";
_config["defaultDomain"] = $.extend(true, _config["defaultDomain"], {
csid: 'ux-en-au'
});
_config["playerOverrides"] = $.extend(true, _config["playerOverrides"], {
widgetID: "network_video_player_freewheel"
});
};
// ninemsn catchup
_marketOverrides[video.enumerations.account.ninemsnCatchup] = function () {
_config["configName"] = "NetworkVideoPlayerFreewheel";
_config["defaultDomain"] = $.extend(true, _config["defaultDomain"], {
csid: 'ux-alt2-en-au'
});
_config["playerOverrides"] = $.extend(true, _config["playerOverrides"], {
widgetID: "network_video_player_freewheel"
});
};
// msn nz
_marketOverrides[video.enumerations.account.msnNZ] = function () {
_config["configName"] = "NetworkVideoPlayer";
_config["defaultDomain"] = $.extend(true, _config["defaultDomain"], {
csid: 'ux-en-nz'
});
_config["playerOverrides"] = $.extend(true, _config["playerOverrides"], {
widgetID: "network_video_player"
});
};
// third Party
_marketOverrides[video.enumerations.account.thirdParty] = function () {
_config["configName"] = "ThirdPartyVideoPlayerFreewheel";
_config["defaultDomain"] = $.extend(true, _config["defaultDomain"], {
csid: 'ux-alt-en-au'
});
_config["playerOverrides"] = $.extend(true, _config["playerOverrides"], {
widgetID: "third_party_video_player_freewheel"
});
};
if (_marketOverrides.hasOwnProperty(_context.config("account"))) {
_marketOverrides[_context.config("account")]();
} else {
_context.logger.error("The supplied account is not supported on this adapter", _context.config("account"));
}
} else {
_context.logger.error("Account has not been set on the adapter");
}
};
var getPlayerOverrides = function () {
/// <summary>Converts standard config items to MSN specific player overrides</summary>
/// <returns type="Object">Player overrides specific to the MSN player</returns>
var _playerOverrides = {};
var _widgetId = _config["playerOverrides"]["widgetID"];
var i;
var _ads = _context.config("ads") || {};
// Configure the advertising
// Are ads enabled?
if (_ads.enabled === false) {
_playerOverrides["AdsAllowed"] = (_ads.enabled ? "VideoContent" : "NoAds");
_playerOverrides["HTML5AdPolicy"] = (_ads.enabled ? "VideoContent" : "NoAds");
} else {
var userConfig = _ads.freewheel || {};
var freewheelConfig = _playerAdapters[video.enumerations.player.adapter.ads.freewheel];
var siteConfig = $.extend({}, _context.config("tracking"), _context.config("networkLocation"));
_playerOverrides["FWSiteSectionId"] = userConfig.siteSection || freewheelConfig.getSiteSection(siteConfig.site, siteConfig.section);
_playerOverrides["FWNetworkId"] = userConfig.networkId || freewheelConfig.getNetworkId();
_playerOverrides["FWServer"] = userConfig.serverUrl || freewheelConfig.getServerUrl();
if (_ads.hasOwnProperty("companions")) {
for (i = 0; i < _ads.companions.length; i++) {
var companion = _ads.companions[i];
if (companion.width == 300 && companion.height == 60) {
_playerOverrides["DisplayAdBanner"] = true;
_playerOverrides["BannerAdDivId"] = companion.outputLocation;
}
}
}
}
// / Configure the advertising
if (_context.config("data") != null) {
// Set the data source
if (_videoFilter.hasOwnProperty(_context.config("data.method"))) {
_playerOverrides[_widgetId + ".DefaultVideo"] = {
videoQuery: {
videoFilter: _videoFilter[_context.config("data.method")]()
}
};
// Sorting
if (_context.config("data.sort") != null) {
var sort = _context.config("data.sort");
var fields = "";
var directions = "";
var translate = function (item) {
// Translate the field
item.by = (_translations.sort_by.hasOwnProperty(item.by) ? _translations.sort_by[item.by] : item.by);
fields += (fields == "") ? item.by : "," + item.by;
// Translate the direction
item.order = (_translations.sort_order.hasOwnProperty(item.order) ? _translations.sort_order[item.order] : item.order);
directions += (directions == "") ? item.order : "," + item.order;
};
if (sort instanceof Array) {
for (i = 0; i < sort.length; i++) {
translate(sort[i]);
}
} else {
translate(sort);
}
_playerOverrides[_widgetId + ".DefaultVideo"].videoQuery.videoFilter["videoSort"] = {
sortField: fields,
sortDirection: directions
};
}
} else {
_context.logger.error("Data method not implemented", _context.config("data"));
}
} else {
_context.logger.warn("An empty data object was supplied to the player.");
}
// Set the player width/height
_playerOverrides[_widgetId + ".Width"] = _context.config("width");
_playerOverrides[_widgetId + ".Height"] = _context.config("height");
// Autoplay the video?
var _autoPlay = _context.config("autoPlay");
if (_autoPlay != null) {
_playerOverrides["AutoPlayVideo"] = _autoPlay;
}
// Enable closed captioning?
var _closedCaptions = _context.config("closedCaptions");
if (_closedCaptions != null) {
_playerOverrides["MsnPlayerDisplayCC"] = _closedCaptions;
}
// Setup the inbuilt player tracking
var _tracking = _context.config("tracking");
if (_tracking != null) {
if (_tracking.hasOwnProperty("site")) {
_playerOverrides["PlayerChannel"] = _tracking.site.replace(' ', '_');
}
if (_tracking.hasOwnProperty("hierarchy")) {
var _hierarchy = "";
for (i = 0; i < _tracking.hierarchy.length; i++) {
var level = _tracking.hierarchy[i].replace(' ', '_');
_hierarchy += (_hierarchy == "") ? level : "^" + level;
}
_playerOverrides["PlayerLocation"] = _hierarchy;
}
}
// Apply any supplied player overrides
_playerOverrides = $.extend(true, _config["playerOverrides"], _playerOverrides);
_context.logger.debug("Player Overrides", _playerOverrides);
return _playerOverrides;
};
var eventFired = function (playerId) {
/// <summary>Handles MSN player events and raises them as ninemsn video events</summary>
/// <returns type="Object" />
/// <private />
var _playerId = playerId;
var _playerEvents = {
// Flash/Silverlight video changed event
currentVideoChanged: function (event) {
if (event.param.hasOwnProperty("status") && event.param.status == "success") {
_context.trigger(video.enumerations.event.video.changed, _context.get("META"));
} else if (event.param.status == "error") {
_context.trigger(video.enumerations.event.video.error);
}
},
// HTML video changed event
currentVideoDataUpdated: function (event) {
if (event.param.status == "success") {
_context.trigger(video.enumerations.event.video.changed, createVideoObject(event.param.video));
} else if (event.param.status == "error") {
_context.trigger(video.enumerations.event.video.error);
}
},
playbackStatusChanged: function (event) {
var _playbackEvents = {
adOpening: function () {
_context.trigger(video.enumerations.event.ad.opening);
},
adPlaying: function () {
_context.trigger(video.enumerations.event.ad.playing);
},
adPaused: function () {
_context.trigger(video.enumerations.event.ad.paused);
},
adPlayFailed: function () {
_context.trigger(video.enumerations.event.ad.error);
},
adPlayCompleted: function () {
_context.trigger(video.enumerations.event.ad.playCompleted);
},
loading: function () {
_context.trigger(video.enumerations.event.video.loading);
},
loaded: function () {
_context.trigger(video.enumerations.event.video.loaded, _context.get("META"));
_videoLoaded = true;
},
ready: function () {
_context.trigger(video.enumerations.event.video.loaded, _context.get("META"));
_videoLoaded = true;
},
videoOpening: function () {
_context.trigger(video.enumerations.event.video.opening);
},
videoPlaying: function () {
_context.trigger(video.enumerations.event.video.playing);
},
videoPaused: function () {
_context.trigger(video.enumerations.event.video.paused, _context.get("POSITION"));
},
playbackStopped: function () {
_context.trigger(video.enumerations.event.video.stopped);
},
videoBuffering: function () {
_context.trigger(video.enumerations.event.video.buffering);
},
playbackCompleted: function () {
_context.trigger(video.enumerations.event.video.playCompleted);
},
videoPlayFailed: function () {
_context.trigger(video.enumerations.event.video.error);
}
};
if (_playbackEvents.hasOwnProperty(event.param.status)) {
_playbackEvents[event.param.status](event);
} else {
_context.logger.warn("'" + event.param.status + "' is an unhandled playback status");
}
}
};
return function (event) {
if (event.sourceId == _playerId) {
if (_playerEvents.hasOwnProperty(event.type)) {
_playerEvents[event.type](event);
} else {
_context.logger.warn(event.type + " is an unhandled event type");
}
}
};
};
var createVideoObject = function (data) {
/// <summary>Converts the MSN video object to a ninemsn video object</summary>
/// <returns type="Object" />
/// <private />
var videoObject = null;
if (data != null && data != "undefined") {
videoObject = {
id: data.uuid,
title: data.title,
description: data.description,
duration: data.durationSecs,
publishDate: video.utils.createUtcDateFromString(data.startDate),
source: data.sourceFriendly,
reporting: {}, // The MSN player does not return usage data
account: video.utils.msn.getAccountFromMarket(data.tags[0].mk),
rawObject: data
};
videoObject.image = function (width, height) {
return "http://img2.catalog.video.msn.com/image.aspx?uuid=" + videoObject.id + "&w=" + width + "&h=" + height;
};
} else {
_context.logger.warn("Cannot normalise, supplied data was invalid", data);
}
return videoObject;
};
var _messageReceivers = [];
var addMessageReceiver = function (eventType, callback) {
/// <summary>Attatches a message receiver to the player</summary>
window.MsnVideo2.addMessageReceiver({ eventType: eventType, widgetId: _outputLocation, widgetGroup: null, funcCb: callback });
// Add it to the array so we can remove it later (if the player is disposed of)
_messageReceivers.push({ eventType: eventType, widgetId: _outputLocation, widgetGroup: null, funcCb: callback });
};
var attachEventHandlers = function () {
/// <summary>Attatches the event handlers to the player</summary>
/// <private />
// Ensure this event is only raised one
window.$vxp("#" + _outputLocation).unbind("playerReady", attachEventHandlers);
// Get the player as a MSN VxP object
var _player = window.$vxp("#" + _id);
// Create the callback handler
var _callback = new eventFired(_id);
// Get the player type (Flash, Silverlight, HTML)
var _playerType = _context.get("TYPE");
// Only attach the handlers if we know the type of player
if (_playerType != null) {
_context.logger.log("Type: " + _playerType);
// The event handlers vary based on the player type
if (_playerType != video.enumerations.player.type.HTML5) {
// Flash/Silverlight player events
addMessageReceiver("currentVideoChanged", _callback);
addMessageReceiver("playbackStatusChanged", _callback);
} else {
// HTML player events
addMessageReceiver("currentVideoDataUpdated", _callback);
addMessageReceiver("playbackStatusChanged",
function (event) {
// The MSN "loaded" event fires too early and the video is unplayable at this time
if (event.param.status != "loaded") {
_callback(event);
}
}
);
// Check to see if the video is paused, if it is the video is ready to play
if ($("#" + _outputLocation).find("div.ready").length > 0) {
// When the player is ready, fire the video:loaded event
_context.one(video.enumerations.event.player.loaded, function () {
_callback({ type: "playbackStatusChanged", sourceId: _id, param: { status: "loaded"} });
});
}
// When the players "canplay" event is fired, then raise a loaded event
_player.find("video").bind("canplay", function () {
_callback({ type: "playbackStatusChanged", sourceId: _id, param: { status: "loaded"} });
});
// window.$vxp.vxpGlobal.players[_id]
}
if (_context.config("msn.playerOverrides.DisplayControlBar") == null || !_context.config("msn.playerOverrides.DisplayControlBar")) {
// The MSN player has a hardcoded div appearing below the player which casues a gap to appear, hide the div
$("#" + _outputLocation + " .vxpMultiLiteInfo").hide();
}
// Raise the player:loaded event
_context.trigger(video.enumerations.event.player.loaded, { id: _outputLocation });
_loaded = true; // The player is now loaded and ready to use
// Raise video:loaded event if video not loaded
if (!_videoLoaded && _playerType != video.enumerations.player.type.HTML5) {
if (_context.get("STATUS") != _translations.event.loading && _context.get("META") != null) {
_callback({ type: "playbackStatusChanged", sourceId: _id, param: { status: "loaded"} });
}
}
// Remove the loading class
$("#" + _outputLocation).removeClass("video_player_loading");
if (_context.config("account") == video.enumerations.account.msnNZ) {
$("#" + _outputLocation).removeClass("video_player_loading_nz");
}
} else {
// Raise a player error event
_context.logger.error("The player type is unknown");
_context.trigger("player:error");
}
};
this.send = function (type, value) {
/// <summary>
/// Convert the type and value to a MSN object and sends it to the playeer
/// </summary>
/// <param name="type" type="String">The type of message to send, such as “PLAY”</param>
/// <param name="value" optional="true" type="Object">The value to send with the message</param>
var params = {
SEEK: function (position) {
return {
"position": position
};
},
VOLUME: function (volume) {
return {
"volume": volume
};
},
MUTE: function (mute) {
return {
"mute": mute
};
},
LOAD: function (uuid) {
return {
"uuid": uuid
};
}
};
window.MsnVideo2.sendMessage({
"type": _translations.send[type],
"targetId": _id,
"param": (params.hasOwnProperty(type) ? params[type](value) : value)
});
};
this.get = function (property) {
/// <summary>
/// Converts the property to a MSN object and gets the value of the given property from the player
/// </summary>
/// <param name="property" type="String">The property to return, such as “VOLUME”</param>
var _methods = {
TYPE: function () {
var _player = window.$vxp.vxpGlobal.players[_id];
if (_player != null) {
var type = _player.type;
var playerTypes = video.enumerations.player.type;
if (type == "msn:flash") {
type = playerTypes.Flash;
} else if (type == "msn:silverlight") {
type = playerTypes.Silverlight;
} else if (type == "msn:html5") {
type = playerTypes.HTML5;
} else {
_context.logger.warn("'" + type + "' is an unknown player type.");
type = null;
}
return type;
} else {
_context.logger.warn("Could not find the player on the page. The player type could not be determined.");
return null;
}
},
VOLUME: function () {
return window.MsnVideo2.getProperties({
"type": _translations.get["VOLUME"],
"targetId": _id
})[0].param.volume;
},
MUTED: function () {
return window.MsnVideo2.getProperties({
"type": _translations.get["MUTED"],
"targetId": _id
})[0].param.mute;
},
POSITION: function () {
return window.MsnVideo2.getProperties({
"type": _translations.get["POSITION"],
"targetId": _id
})[0].param.position;
},
DURATION: function () {
return window.MsnVideo2.getProperties({
"type": _translations.get["DURATION"],
"targetId": _id
})[0].param.duration;
},
STATUS: function () {
if (_loaded) {
var status = window.MsnVideo2.getProperties({
"type": _translations.get["STATUS"],
"targetId": _id
})[0].param.status;
// fix Silverlight player has capital play status
if (_context.get("TYPE") == video.enumerations.player.type.Silverlight) {
status = status.substring(0, 1).toLowerCase() + status.substring(1);
}
if (_translations.event[status] != null) {
return _translations.event[status];
} else {
_context.logger.warn("Could not normalise the '" + status + "' status");
return null;
}
}
else {
return "player:loading";
}
},
META: function () {
return createVideoObject(
window.MsnVideo2.getProperties({
"type": _translations.get["META"],
"targetId": _id
})[0].param.video
);
},
FULLSCREEN: function () {
return window.MsnVideo2.getProperties({
"type": _translations.get["FULLSCREEN"],
"targetId": _id
})[0].param.isFullScreen;
}
};
if (_methods.hasOwnProperty(property)) {
return _methods[property]();
} else {
_context.logger.warn(property + " is not a supported property");
return null;
}
};
this.dispose = function () {
/// <summary>
/// Removes the player from the page
/// </summary>
_context.logger.log("Removing a MSN player");
// Remove all event handlers
for (var i = 0; i < _messageReceivers.length; i++) {
window.MsnVideo2.removeMessageReceiver(_messageReceivers[i]);
}
_messageReceivers = []; // Empty the array
// Call the MSN remove method
window.MsnVideoUx.remove(_outputLocation);
};
// Handles the configuration and creation of the MSN player
(function () {
// Raise the loading event
_context.trigger(video.enumerations.event.player.loading);
_outputLocation = _context.config("outputLocation");
// There is a known issue in the MSN HTML5 player when the player is rendered in a location called "videoplayer"
if (_outputLocation.toLowerCase() == "videoplayer") {
_context.logger.warn("Output location cannot be '" + _outputLocation + "'. Please change the name of the output location.");
}
/*
The MSN player has issues resizing. If the output width is different to the network default,
a background is automatically added. This clipping removes the background.
*/
$("#" + _outputLocation).css({
overflow: _config["overflow"],
width: (function () {
return _context.config("width") + "px";
} ()),
height: (function () {
return _context.config("height") + "px";
} ())
});
// Add the loading class
$("#" + _outputLocation).addClass("video_player_loading");
if (_context.config("account") == video.enumerations.account.msnNZ) {
$("#" + _outputLocation).addClass("video_player_loading_nz");
}
// The ID of the player object, a bit hacky but this is the only way to get the ID of the player before it is created
_id = _outputLocation + "_ux1_1_1_1";
// Set market specific config overrides
setMarketOverrides();
// Once the component is loaded; attatch the event handlers
window.$vxp("#" + _outputLocation).bind("playerReady", attachEventHandlers);
// Render the MSN player
_context.logger.log("Rendering a MSN video player");
window.MsnVideoUx.render(
(_context.config("msn") && _context.config("msn")["configName"]) || _config["configName"],
_outputLocation,
getPlayerOverrides(),
(_context.config("msn") && _context.config("msn")["defaultDomain"]) || _config["defaultDomain"]
);
})();
};
_playerAdapters[video.enumerations.player.adapter.Brightcove] = _playerAdapters[video.enumerations.player.adapter.Brightcove] || function (playerContext) {
/// <summary>Creates a new ninemsn.portal.common.video.player.adapters.msn object</summary>
/// <returns type="ninemsn.portal.common.video.player">A Brightcove video player object</returns>
// Get the player's context
var _context = playerContext;
/// The default configuration of the player extended by the supplied settings
var _config = $.extend(
true,
{
bgcolor: "#000000",
isVid: true,
isUI: true,
dynamicStreaming: true,
includeAPI: true,
wmode: "transparent"
},
_context.config("brightcove")); // the configuration of the player
var _outputLocation = _context.config("outputLocation");
var _experienceID = "BC_" + _outputLocation;
var _player; // The player as a brightcove experience
var _modVP; // Video Player Module
var _modExp; // Experience Module
var _modCon; // Content Module
var _modAd; // Advertising Module
var _loaded = false;
var _currentVideo; // Stores the META data for the current video
var _currentPosition = 0; // Stores the current time position
var _currentStatus = video.enumerations.event.player.loading; // Stores the status of the player/video
var _translate = {
Age: { MA: 15, R: 18 },
AudienceInfo: { PG: "Parental guidance recommended", M: "Recommended for mature audiences" }
};
this.send = function (type, value) {
var methods = {
SEEK: function (position) {
_modVP.seek(position);
},
LOAD: function (id) {
id = id.toString();
if (id.indexOf("ref:") != 0) {
_modVP.loadVideoByID(id);
} else {
// remove the "ref:" prefix
id = id.replace("ref:", "");
// load by reference id
_modVP.loadVideoByReferenceID(id);
}
$("#video_player_ageGate").remove();
$("iframe#" + _experienceID).css({ width: _context.config("width") + "px" });
_context.one(video.enumerations.event.video.loaded, attachAgeGate);
$("#video_player_html5_error").remove();
},
PLAY: function () {
_modVP.play();
},
PAUSE: function () {
_modVP.pause();
},
STOP: function () {
_modVP.pause();
_modVP.seek(0);
_context.trigger(video.enumerations.event.video.stopped);
}
};
if (methods.hasOwnProperty(type)) {
methods[type](value);
} else {
_context.logger.warn(type + " is not supported");
}
};
this.get = function (property) {
/// <summary>
/// Converts the property to a MSN object and gets the value of the given property from the player
/// </summary>
/// <param name="property" type="String">The property to return, such as “VOLUME”</param>
var _methods = {
META: function () {
return _currentVideo;
},
TYPE: function () {
if (_modVP.experience != null) {
var type = _modVP.experience.type;
var playerTypes = video.enumerations.player.type;
if (type == "flash") {
type = playerTypes.Flash;
} else if (type == "html") {
type = playerTypes.HTML5;
} else {
_context.logger.warn("'" + type + "' is an unknown player type.");
type = null;
}
return type;
} else {
_context.logger.warn("Could not find the player on the page. The player type could not be determined.");
return null;
}
},
POSITION: function () {
return _currentPosition;
},
DURATION: function () {
if (_currentVideo != null) {
return _currentVideo.duration;
} else {
_context.logger.warn("DURATION is not available yet");
return 0;
}
},
STATUS: function () {
if (_loaded) {
return _currentStatus;
}
else {
return video.enumerations.event.player.loading;
}
}
};
if (_methods.hasOwnProperty(property)) {
return _methods[property]();
} else {
_context.logger.warn(property + " is not a supported property");
return null;
}
};
var createVideoObject = function (data) {
/// <summary>Converts the Brightcove video object to a ninemsn video object</summary>
/// <returns type="Object" />
/// <private />
var videoObject = null;
if (data != null && data != "undefined") {
videoObject = {
id: data.id,
title: data.displayName,
description: data.shortDescription,
duration: data.length / 1000, // convert the duration to seconds
publishDate: video.utils.createUtcDateFromString(data.startDate),
source: data && data.customFields && data.customFields.series,
account: _context.config("account"),
reporting: {}, // The Brightcove player does not return usage data
rawObject: data
};
videoObject.image = function (width, height) {
return "http://images.ninemsn.com.au/resizer.aspx?width=" + width + "&height=" + height + "&url=" + data.videoStillURL;
};
} else {
_context.logger.warn("Cannot normalise, supplied data was invalid", data);
}
return videoObject;
};
var eventFired = function () {
/// <summary>Handles Brightcove player events and raises them as ninemsn video events</summary>
/// <returns type="Object" />
/// <private />
var _playerEvents = {
mediaPlay: function (event) {
if (event.position == 0) {
_currentVideo = createVideoObject(event.media);
_currentStatus = video.enumerations.event.video.opening;
if (_currentVideo != null) {
_context.trigger(video.enumerations.event.video.opening, _currentVideo);
} else {
_context.logger.warn("Could not raise video:opening with META data as it was null");
_context.trigger(video.enumerations.event.video.opening);
}
}
_currentStatus = video.enumerations.event.video.playing;
_context.trigger(video.enumerations.event.video.playing);
},
mediaStop: function () {
_currentStatus = video.enumerations.event.video.paused;
_context.trigger(video.enumerations.event.video.paused);
},
mediaComplete: function () {
_currentStatus = video.enumerations.event.video.playCompleted;
_context.trigger(video.enumerations.event.video.playCompleted);
},
mediaChange: function (event) {
_currentVideo = createVideoObject(event.media);
_currentStatus = video.enumerations.event.video.changed;
if (_currentVideo != null) {
_context.trigger(video.enumerations.event.video.changed, _currentVideo);
_context.trigger(video.enumerations.event.video.loaded, _currentVideo);
} else {
// Get the current video
_modVP.getCurrentVideo(function (result) {
_currentVideo = createVideoObject(result);
_context.trigger(video.enumerations.event.video.changed, _currentVideo);
_context.trigger(video.enumerations.event.video.loaded, _currentVideo);
});
}
},
mediaProgress: function (event) {
// store the current position
_currentPosition = event.position;
},
mediaError: function () {
_currentStatus = video.enumerations.event.video.error;
_context.trigger(video.enumerations.event.video.error);
},
adStart: function (event) {
if (event.position == 0) {
_currentStatus = video.enumerations.event.ad.opening;
_context.trigger(video.enumerations.event.ad.opening);
}
_currentStatus = video.enumerations.event.ad.playing;
_context.trigger(video.enumerations.event.ad.playing);
},
adComplete: function () {
_currentStatus = video.enumerations.event.ad.playCompleted;
_context.trigger(video.enumerations.event.ad.playCompleted);
}
};
return function (event) {
if (_playerEvents.hasOwnProperty(event.type)) {
_playerEvents[event.type](event);
} else {
_context.logger.warn(event.type + " is an unhandled event type");
}
};
};
var attachEventHandlers = function () {
_player = window.brightcove.api.getExperience(_experienceID);
_currentStatus = video.enumerations.event.player.loaded;
// Create the callback handler
var _callback = new eventFired();
_modVP = _player.getModule(window.brightcove.api.modules.APIModules.VIDEO_PLAYER);
_modExp = _player.getModule(window.brightcove.api.modules.APIModules.EXPERIENCE);
_modCon = _player.getModule(window.brightcove.api.modules.APIModules.CONTENT);
_modAd = _player.getModule(window.brightcove.api.modules.APIModules.ADVERTISING);
_context.trigger(video.enumerations.event.video.loading);
_currentStatus = video.enumerations.event.video.loading;
_modExp.addEventListener(window.brightcove.api.events.ExperienceEvent.TEMPLATE_READY, function () {
_loaded = true;
_modVP.addEventListener(window.brightcove.api.events.MediaEvent.BEGIN, _callback);
_modVP.addEventListener(window.brightcove.api.events.MediaEvent.CHANGE, _callback);
_modVP.addEventListener(window.brightcove.api.events.MediaEvent.COMPLETE, _callback);
_modVP.addEventListener(window.brightcove.api.events.MediaEvent.ERROR, _callback);
_modVP.addEventListener(window.brightcove.api.events.MediaEvent.PLAY, _callback);
_modVP.addEventListener(window.brightcove.api.events.MediaEvent.PROGRESS, _callback);
_modVP.addEventListener(window.brightcove.api.events.MediaEvent.STOP, _callback);
_modAd.addEventListener(window.brightcove.api.events.AdEvent.START, _callback);
_modAd.addEventListener(window.brightcove.api.events.AdEvent.COMPLETE, _callback);
});
};
var setAccountOverrides = function () {
/// <summary>Updates the config object to include account specific overrides</summary>
if (_context.config("account") != null) {
var _accountOverrides = {};
// ninemsn catchup
_accountOverrides[video.enumerations.account.ninemsnCatchup] = function () {
_config["playerID"] = "2193241313001";
_config["playerKey"] = "AQ~~,AAABecFwRRk~,e1HkYhZIbpgkrcvm9Xo3tHIAN8ttykr_";
// Dev
//_config["playerID"] = "929577932001";
//_config["playerKey"] = "AQ~~,AAAA2EMGPBE~,w0dWIowaawiPXdUORDVzo9p-LKLkiBxg";
};
// ninemsn
_accountOverrides[video.enumerations.account.ninemsn] = function () {
_config["playerID"] = "952317942001";
_config["playerKey"] = "AQ~~,AAAAmtNDn-E~,a88FbwlXndisf2n9Aq-Kj8HVijl5N4vU";
};
if (_accountOverrides.hasOwnProperty(_context.config("account"))) {
_accountOverrides[_context.config("account")]();
} else {
_context.logger.error("The supplied account is not supported on this adapter", _context.config("account"));
}
} else {
_context.logger.error("Account has not been set on the adapter");
}
};
var preloadClassificationImg = function () {
function preload(arrayOfImages) {
$(arrayOfImages).each(function () {
$('<img/>')[0].src = this;
});
}
// Preload classification imgs:
preload([
'http://shared.9msn.com.au/share/long_cache/img/video/age_gate/M.png',
'http://shared.9msn.com.au/share/long_cache/img/video/age_gate/MA.png',
'http://shared.9msn.com.au/share/long_cache/img/video/age_gate/PG.png',
'http://shared.9msn.com.au/share/long_cache/img/video/age_gate/R.png',
'http://shared.9msn.com.au/share/long_cache/img/video/age_gate/black_bg.gif',
'http://shared.9msn.com.au/share/long_cache/img/video/age_gate/dialog_bg.png',
'http://shared.9msn.com.au/share/long_cache/img/video/age_gate/M_bg.jpg',
'http://shared.9msn.com.au/share/long_cache/img/video/age_gate/PG_bg.jpg'
]);
};
var attachAgeGate = function () {
var ageGateHTML = "";
var classification = _currentVideo.rawObject.customFields.classification;
var autoPlay = _context.config("autoPlay");
function calaulateAge() {
if (classification == "MA" || classification == "R") {
if (video.utils.isDate($("#video_player_ageGate input#day").val() + "/" + $("#video_player_ageGate input#month").val() + "/" + $("#video_player_ageGate input#year").val())) {
var birthDate = new Date(parseInt($("#video_player_ageGate input#year").val()), parseInt($("#video_player_ageGate input#month").val()) - 1, parseInt($("#video_player_ageGate input#day").val()));
var today = new Date();
var restrictYear = _translate.Age.hasOwnProperty(classification) ?
_translate.Age[classification] : 0;
if (today >= new Date(birthDate.getFullYear() + restrictYear, birthDate.getMonth(), birthDate.getDate())) {
$("#video_player_ageGate").remove();
$("iframe#" + _experienceID).css({ width: _context.config("width") + "px" });
_modVP.play();
} else {
$("#video_player_ageGate span.warning").text("We're sorry, this video is restricted based on the date of birth you entered.");
setTimeout(function () {
for (var vid in video.objects) {
if (video.objects[vid].config().outputLocation == _outputLocation) {
video.objects[vid].trigger(video.enumerations.event.video.playCompleted);
}
}
}, 5000);
}
} else {
$("#video_player_ageGate span.warning").text("Invalid date input.");
}
} else {
$("#video_player_ageGate").remove();
$("iframe#" + _experienceID).css({ width: _context.config("width") + "px" });
_modVP.pause(false);
}
}
if (classification &&
(classification == "M" || classification == "MA" || classification == "PG" || classification == "R")
) {
ageGateHTML = "<div id='video_player_ageGate' class='rate_" + classification.toLowerCase() + "' style='display:none;'>";
ageGateHTML += "<span class='rateinfo'><img src='http://shared.9msn.com.au/share/long_cache/img/video/age_gate/" + classification + ".png'>";
ageGateHTML += "</span>";
if (classification == "MA" || classification == "R") {
ageGateHTML += "<div class='agedialog'>";
ageGateHTML += "<span class='ageinfo'>Please verify your date of birth to view this video</span>";
ageGateHTML += "<span class='dobinput'><span class='dayinput'><input id='day' type='text' maxlength='2' value='DD'/></span>";
ageGateHTML += "<input id='month' type='text' maxlength='2' value='MM'/>";
ageGateHTML += "<input id='year' type='text' maxlength='4' value='YYYY'/></span>";
ageGateHTML += "</div>";
ageGateHTML += "<input id='submit' type='button' value=''></input>";
}
else {
ageGateHTML += "<span class='audienceinfo'>" + _translate.AudienceInfo[classification].toString() + "</span>";
}
ageGateHTML += "<span class='warning'></span>";
ageGateHTML += "</div>";
if ($("#" + _outputLocation + " #video_player_ageGate").length == 0) {
$("#" + _outputLocation).append(ageGateHTML);
$("#video_player_ageGate input[type=text]").click(function (event) { $(this).val(""); });
$("#video_player_ageGate input[type=text]#day").blur(function (e) { if ($(this).val() == "") $(this).val("DD"); });
$("#video_player_ageGate input[type=text]#month").blur(function (e) { if ($(this).val() == "") $(this).val("MM"); });
$("#video_player_ageGate input[type=text]#year").blur(function (e) { if ($(this).val() == "") $(this).val("YYYY"); });
$("#video_player_ageGate input[type=text]#day").keyup(function (e) { if ($(this).val().length == 2) $("#video_player_ageGate input[type=text]#month").click().focus(); });
$("#video_player_ageGate input[type=text]#month").keyup(function (e) { if ($(this).val().length == 2) $("#video_player_ageGate input[type=text]#year").click().focus(); });
$("#video_player_ageGate #submit").click(calaulateAge);
$("#video_player_ageGate input[type=text]").keypress(function (event) {
if (event.keyCode == 13) {
calaulateAge(); return false;
}
});
$("#video_player_ageGate").css({ width: _context.config("width") + "px", height: _context.config("height") + "px" });
}
// Attach video:playing to show classification, only to flash player
if ((classification == "M" || classification == "PG") && _context.get("TYPE") == video.enumerations.player.type.Flash) {
_context.one('video:playing', function () {
$("#video_player_ageGate").show();
if ($("#video_player_ageGate").length > 0) {
setTimeout(function () {
_modVP.pause();
}, 500);
setTimeout(function () { calaulateAge(); }, 2500);
$("iframe#" + _experienceID).css({ width: "0px" });
}
});
if (autoPlay) {
_modVP.play();
}
}
else if (classification == "MA" || classification == "R") // for MA or R, show age gate directly for both flash and HTML5
{
$("#video_player_ageGate").show();
if ($("#video_player_ageGate").length > 0) {
setTimeout(function () {
_modVP.pause();
}, 500);
$("iframe#" + _experienceID).css({ width: "0px" });
}
}
}
else { // No Classification
if (autoPlay) {
_modVP.play();
}
}
};
var attachLoadImg = function () {
if (_context.get("TYPE") == video.enumerations.player.type.Flash) {
if ($("#" + _outputLocation + " #video_player_brightcove_loading_img").length == 0) {
$("#" + _outputLocation).append("<img src='http://catchup.ninemsn.com.au/img/loading.gif' id='video_player_brightcove_loading_img'/>");
} else {
$("#video_player_brightcove_loading_img").show();
}
_context.one("ad:playing video:playing player:error", function () {
$("#video_player_brightcove_loading_img").hide();
});
}
};
var attachHTML5Error = function () {
if (_context.get("TYPE") == video.enumerations.player.type.HTML5) {
var defaultErrorHTML = "";
defaultErrorHTML = "<div id='video_player_html5_error'>";
defaultErrorHTML += "<span>We’re sorry, the video you are looking for cannot be viewed on this device.</span>";
defaultErrorHTML += "</div>";
if ($("#" + _outputLocation + " #video_player_html5_error").length == 0) {
$("#" + _outputLocation).append(defaultErrorHTML);
$("#video_player_html5_error").css({ width: _context.config("width") + "px", height: _context.config("height") + "px" });
} else {
$("#video_player_html5_error").show();
}
}
};
var setupAds = function () {
// Configure the advertising