Created
May 23, 2013 08:56
-
-
Save adammw/5633645 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | |
var adsConfig = _context.config("ads") || {}; | |
// Are ads enabled? | |
if (adsConfig.enabled === false) { | |
return; | |
} | |
var userInput = adsConfig.freewheel || {}; | |
var freewheelConfig = _playerAdapters[video.enumerations.player.adapter.ads.freewheel]; | |
var siteConfig = $.extend({}, _context.config("tracking"), _context.config("networkLocation")); | |
//settings for flash player | |
var defaults = { | |
fw_server: userInput.serverUrl || freewheelConfig.getServerUrl(), | |
amLocation: userInput.adManagerUrl || freewheelConfig.getAdManagerUrl('flash'), //fixed | |
networkId: userInput.networkId || freewheelConfig.getNetworkId(), //account | |
siteSection: userInput.siteSection || freewheelConfig.getSiteSection(siteConfig.site, siteConfig.section), //site info | |
cb_profile: userInput.playerProfile || freewheelConfig.getPlayerProfile('flash', 'brightcove'), //fixed (different for html5) | |
assetIdField: userInput.assetIdField || 'standardBC_id', //fixed | |
fw_keyvalues: userInput.keyValues, | |
sponsorMsg: true | |
}; | |
userInput = $.extend(true, userInput, adsConfig.html5 && adsConfig.html5.freewheel); | |
//settings for html player | |
var htmlSettings = userInput.html || { }; | |
defaults = $.extend(defaults, { | |
serverUrl: defaults.serverUrl, | |
amLocation_js: htmlSettings.adManagerUrl || freewheelConfig.getAdManagerUrl('html'), | |
siteSectionId: defaults.siteSection, | |
cb_profile_js: htmlSettings.playerProfile || freewheelConfig.getPlayerProfile('html', 'brightcove'), | |
videoAssetCustomId: _config["@videoPlayer"], | |
keyvalues: userInput.keyValues | |
}); | |
var config = $.extend(true, defaults, adsConfig && adsConfig.freewheel); | |
window.fw_config = function () { | |
return config; | |
}; | |
}; | |
var setPlayerOverrides = function () { | |
var _playerOverrides = {}; | |
if (_context.config("data") != null) { | |
var _videoFilter = {}; | |
_videoFilter[video.enumerations.data.method.ID] = function () { | |
return _context.config("data.filter.id"); | |
}; | |
_videoFilter[video.enumerations.data.method.Tags] = function () { | |
_context.one(video.enumerations.event.player.loaded, | |
function () { | |
var dt = new video.Data( | |
video.$.extend(true, { | |
success: function (data) { | |
_modVP.cueVideoByID(data.videos[0].id); | |
} | |
}, _context.config("data")) | |
); | |
}); | |
return ""; | |
}; | |
// Set the data source | |
if (_videoFilter.hasOwnProperty(_context.config("data.method"))) { | |
_playerOverrides["@videoPlayer"] = _videoFilter[_context.config("data.method")](); | |
videoId = _playerOverrides["@videoPlayer"]; | |
// Attach Age Gate for video.Playing | |
_context.one(video.enumerations.event.video.loaded, attachAgeGate); | |
// Attach Loading Img | |
//if (_context.config("account") === video.enumerations.account.ninemsnCatchup) { | |
// _context.on(video.enumerations.event.video.loaded, attachLoadImg); | |
//} | |
// Attach HTML5 player error message | |
_context.on(video.enumerations.event.video.error, attachHTML5Error); | |
} 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["width"] = _context.config("width"); | |
_playerOverrides["height"] = _context.config("height"); | |
// Autoplay the video? - moved to age gate logic | |
//var _autoPlay = _context.config("autoPlay"); | |
//if (_autoPlay != null) { | |
// _playerOverrides["autoStart"] = _autoPlay; | |
//} | |
// Apply the supplied player overrides | |
_config = $.extend(true, _config, _playerOverrides); | |
_context.logger.debug("Player Configuration", _config); | |
}; | |
function createParam(name, value) { | |
return "<param name=\"" + name + "\" value=\"" + value + "\" />"; | |
}; | |
function attachTemplateHandlers() { | |
var _markup = ""; | |
// Set the callback for when the player is loaded | |
// we need to create a new function and attach it to the window to support the HTML5 player | |
window[_experienceID + "_loadCallback"] = (function () { | |
// raise the player:loaded event | |
_context.trigger(video.enumerations.event.player.loaded); | |
// Remove this function from the DOM | |
delete window[_experienceID + "_loadCallback"]; | |
}); | |
_markup += createParam("templateLoadHandler", (_experienceID + "_loadCallback")); | |
window[_experienceID + "_readyCallback"] = (function () { | |
// Get the current video | |
_modVP.getCurrentVideo(function (result) { | |
_currentVideo = createVideoObject(result); | |
_context.trigger(video.enumerations.event.video.loaded, _currentVideo); | |
}); | |
// Remove this function from the DOM | |
delete window[_experienceID + "_readyCallback"]; | |
}); | |
_markup += createParam("templateReadyHandler", (_experienceID + "_readyCallback")); | |
window[_experienceID + "_errorCallback"] = (function (event) { | |
_currentStatus = video.enumerations.event.player.error; | |
var _errors = { | |
unknown: function () { | |
_context.trigger(video.enumerations.event.player.error); | |
_context.logger.error(_currentStatus + " | There was an unidentifiable issue that prevents load or playback", event); | |
}, | |
domainRestricted: function () { | |
_context.trigger(video.enumerations.event.player.error); | |
_context.logger.error(_currentStatus + " | The player is not allowed to be viewed on the requesting domain", event); | |
}, | |
geoRestricted: function () { | |
_context.trigger(video.enumerations.event.player.error); | |
_context.logger.error(_currentStatus + " | The player may not be viewed in the current geography", event); | |
}, | |
invalidID: function () { | |
_context.trigger(video.enumerations.event.player.error); | |
_context.logger.error(_currentStatus + " | The ID provided for the player is not for a currently active player", event); | |
}, | |
noContent: function () { | |
_context.trigger(video.enumerations.event.player.error); | |
_context.logger.error(_currentStatus + " | There was no programmed content in the player", event); | |
}, | |
unavailableContent: function () { | |
_currentStatus = video.enumerations.event.video.error; | |
_context.trigger(video.enumerations.event.video.error); | |
_context.logger.error(_currentStatus + " | There was an error loading the video content", event); | |
}, | |
upgradeRequiredForVideo: function () { | |
_context.trigger(video.enumerations.event.player.error); | |
_context.logger.error(_currentStatus + " | An upgrade to the Flash Player is required to view the content", event); | |
}, | |
upgradeRequiredForPlayer: function () { | |
_context.trigger(video.enumerations.event.player.error); | |
_context.logger.error(_currentStatus + " | An upgrade to the Flash Player is required to load the player", event); | |
}, | |
serviceUnavailable: function () { | |
_context.trigger(video.enumerations.event.player.error); | |
_context.logger.error(_currentStatus + " | There was an error connecting to the backend on initial load", event); | |
} | |
}; | |
if (_errors.hasOwnProperty(event.errorType)) { | |
_errors[event.errorType](); | |
} else { | |
// Raise an "unknown" error event | |
_errors["unknown"](); | |
} | |
}); | |
_markup += createParam("templateErrorHandler", (_experienceID + "_errorCallback")); | |
return _markup; | |
} | |
var videoId; | |
// Handles the configuration and creation of the Brightcove player | |
(function () { | |
// Raise the loading event | |
_context.trigger(video.enumerations.event.player.loading); | |
// Preload Classification Images | |
preloadClassificationImg(); | |
// Once the player is loaded, attach the event handlers | |
_context.on(video.enumerations.event.player.loaded, function () { | |
attachEventHandlers(); | |
}); | |
// Set the account overrides | |
setAccountOverrides(); | |
// Set the player overrides | |
setPlayerOverrides(); | |
setupAds(); | |
_config = $.extend(true, _config, _context.config("brightcove")); | |
// Render the player | |
var _markup = "<object id=\"" + _experienceID + "\" class=\"BrightcoveExperience\">"; | |
for (var i in _config) { | |
_markup += createParam(i, _config[i]); | |
} | |
// Attach event handlers to the template | |
_markup += attachTemplateHandlers(); | |
_markup += "</object>"; | |
$("#" + _outputLocation).html(_markup); | |
var head = document.getElementById(_outputLocation); | |
var scr = document.createElement('script'); | |
scr.setAttribute('src', 'http://adm.fwmrm.net/p/mi9_test/BrightcovePlugin.js'); | |
var freewheelPluginLoaded = false; | |
scr.onload = scr.onreadystatechange = function () { | |
if (!freewheelPluginLoaded && (!this.readyState || this.readyState == 'complete')) { | |
freewheelPluginLoaded = true; | |
window.brightcove.createExperiences(); | |
scr.onload = null; | |
} | |
}; | |
head.appendChild(scr); | |
})(); | |
}; | |
_playerAdapters[video.enumerations.player.adapter.ads.freewheel] = | |
_playerAdapters[video.enumerations.player.adapter.ads.freewheel] || (function () { | |
var siteSectionMappings = { | |
'channelnine': { | |
'sections': { | |
'showthefarmerwantsawife': {} | |
} | |
}, | |
'homes': { | |
'sections': { | |
'houseandgarden': { }, | |
'realliving': { }, | |
'theblock2012': { } | |
} | |
} | |
}; | |
var methods = {}; | |
methods.getSiteSection = function (site, section) { | |
var selectedSiteSectionName = site + '_general'; | |
var siteMap = siteSectionMappings[site]; | |
if (siteMap) { | |
if (siteMap.sections && siteMap.sections[section]) { | |
selectedSiteSectionName = siteMap.sections[section].name || | |
site + '_' + section + '_general'; | |
} | |
if (siteMap.name) | |
selectedSiteSectionName = siteMap.name; | |
} | |
return selectedSiteSectionName; | |
}; | |
methods.getNetworkId = function () { | |
return 375613; | |
}; | |
methods.getPlayerProfile = function (playerType, playerVendor) { | |
if (playerType === 'flash') | |
return playerVendor === 'msn' ? 'MSN_AU_as3_Live' : 'MSN_AU_BC_Live'; | |
else | |
return playerVendor === 'msn' ? 'MSN_AU_HTML5_SmartXML_Live' : 'MSN_AU_BC_HTML5_Live'; | |
}; | |
methods.getAdManagerUrl = function (playerType) { | |
return playerType === 'html' ? 'http://adm.fwmrm.net/p/msn_au_live/AdManager.js' : | |
'http://adm.fwmrm.net/p/msn_au_live/AdManager.swf?logLevel=QUIET&cb=1'; | |
}; | |
methods.getServerUrl = function () { | |
return 'http://5bb3d.v.fwmrm.net'; | |
}; | |
return methods; | |
} ()); | |
video.utils = video.utils || { | |
createDataFilterFromQuery: function (query) { | |
/// <summary>Translates a MSN video query to data objects</summary> | |
query = query.indexOf("/") == 0 ? query.substring(1) : query; | |
query = decodeURIComponent(query); | |
var _translations = { | |
sort_by: { | |
ActiveStartDate: "START_DATE" | |
}, | |
market: { | |
"en-au": video.enumerations.account.ninemsn, | |
"en-nz": video.enumerations.account.msnNZ, | |
"alt2-en-au": video.enumerations.account.ninemsnCatchup, | |
"alt-en-au": video.enumerations.account.thirdParty | |
} | |
}; | |
var paramsAsObject = function (q) { | |
/// <summary>Converts the params of a URL into an object</summary> | |
// Remove everything before the ? | |
q = q.substr(q.indexOf('?') + 1); | |
// Split everything on & | |
q = q.split('&'); | |
// Split all items on = | |
var params = {}; | |
for (i = 0; i < q.length; i++) { | |
var param = q[i].split('='); | |
params[param[0]] = param[1]; | |
} | |
return params; | |
}; | |
var data = {}; | |
var queryParams = paramsAsObject(query); | |
var i; | |
// From video by tags query | |
if (query.toLowerCase().indexOf("videobytag.aspx") > -1) { | |
data["method"] = video.enumerations.data.method.Tags; | |
data["filter"] = { tags: [] }; | |
//ns=MSNVideo_Cat&tag=AUnationalninenews | |
// [ { $namespace: "", $: "" }, ... ] | |
var ns = queryParams["ns"].split(','); | |
var tags = queryParams["tag"].split(','); | |
for (i = 0; i < ns.length; i++) { | |
data["filter"]["tags"].push({ name: ns[i], value: tags[i] }); | |
} | |
} | |
// From related video query | |
if (query.toLowerCase().indexOf("relatedvideos.aspx") > -1) { | |
data["method"] = video.enumerations.data.method.Related; | |
data["filter"] = { id: queryParams["uuids"] }; | |
} | |
// From search query | |
if (query.toLowerCase().indexOf("search.aspx") > -1) { | |
data["method"] = video.enumerations.data.method.Search; | |
data["filter"] = { term: [] }; | |
var terms = queryParams["q"].split(' '); | |
for (i = 0; i < terms.length; i++) { | |
data["filter"]["term"].push(terms[i]); | |
} | |
} | |
// Paging | |
if (queryParams.hasOwnProperty("ps")) { | |
data["paging"] = {}; | |
data["paging"]["size"] = queryParams["ps"]; | |
var pageSize = parseInt(data["paging"]["size"]); | |
if (queryParams.hasOwnProperty("ind")) { | |
data["paging"] = (typeof (data["paging"]) == "undefined") ? {} : data["paging"]; | |
var pageNumber = parseInt(queryParams["ind"]) - 1; | |
pageNumber = pageNumber + pageSize; | |
pageNumber = pageNumber / pageSize; | |
data["paging"]["page"] = pageNumber; | |
} | |
} | |
// Market | |
if (queryParams.hasOwnProperty("mk")) { | |
var mk = queryParams["mk"]; | |
data["account"] = _translations.market.hasOwnProperty(mk) ? _translations.market[mk] : mk; | |
} | |
// File Filter | |
if (queryParams.hasOwnProperty("ff")) { | |
data["msn"] = {}; | |
data["msn"]["fileFilter"] = queryParams["ff"]; | |
} | |
// Sorting | |
if (queryParams.hasOwnProperty("sf")) { | |
var sf = queryParams["sf"].split(','); | |
var sd = (queryParams.hasOwnProperty("sd")) ? queryParams["sd"].split(',') : []; | |
var sort = []; | |
for (i = 0; i < sf.length; i++) { | |
var by = (_translations.sort_by.hasOwnProperty(sf[i]) ? _translations.sort_by[sf[i]] : sf[i]); | |
var order = ((sd[i] < sd.length) ? sd[i] : 1); | |
order = ((order == -1) ? "DESC" : "ASC"); | |
sort.push({ | |
"by": by, | |
"order": order | |
}); | |
} | |
data["sort"] = sort; | |
} | |
return data; | |
}, | |
createUtcDateFromString: function (fullUtcDate) { | |
if (typeof fullUtcDate == "string") { | |
var dateToReturn; | |
if (fullUtcDate.indexOf('T') > -1) { | |
if (fullUtcDate.indexOf('Z') > -1) { | |
fullUtcDate = fullUtcDate.substr(0, fullUtcDate.length - 1); // remove the Z | |
} | |
var utcDate = fullUtcDate.split('T')[0].split('-'); | |
var utcTime = fullUtcDate.split('T')[1].split(':'); | |
dateToReturn = new Date(Date.UTC(utcDate[0], (parseInt(utcDate[1], 10) - 1), utcDate[2], utcTime[0], utcTime[1], utcTime[2])); | |
} else { | |
dateToReturn = new Date(fullUtcDate); | |
} | |
return dateToReturn; | |
} else { | |
return fullUtcDate; | |
} | |
}, | |
isDate: function (value) { | |
try { | |
//Change the below values to determine which format of date you wish to check. It is set to dd/mm/yyyy by default. | |
var DayIndex = 0; | |
var MonthIndex = 1; | |
var YearIndex = 2; | |
value = value.replace(/-/g, "/").replace(/\./g, "/"); | |
var SplitValue = value.split("/"); | |
var OK = true; | |
if (!(SplitValue[DayIndex].length == 1 || SplitValue[DayIndex].length == 2)) { | |
OK = false; | |
} | |
if (OK && !(SplitValue[MonthIndex].length == 1 || SplitValue[MonthIndex].length == 2)) { | |
OK = false; | |
} | |
if (OK && SplitValue[YearIndex].length != 4) { | |
OK = false; | |
} | |
if (OK) { | |
var Day = parseInt(SplitValue[DayIndex], 10); | |
var Month = parseInt(SplitValue[MonthIndex], 10); | |
var Year = parseInt(SplitValue[YearIndex], 10); | |
if (OK = ((Year > 1900) && (Year < new Date().getFullYear()))) { | |
if (OK = (Month <= 12 && Month > 0)) { | |
var LeapYear = (((Year % 4) == 0) && ((Year % 100) != 0) || ((Year % 400) == 0)); | |
if (Month == 2) { | |
OK = LeapYear ? Day <= 29 : Day <= 28; | |
} | |
else { | |
if ((Month == 4) || (Month == 6) || (Month == 9) || (Month == 11)) { | |
OK = (Day > 0 && Day <= 30); | |
} | |
else { | |
OK = (Day > 0 && Day <= 31); | |
} | |
} | |
} | |
} | |
} | |
return OK; | |
} | |
catch (e) { | |
return false; | |
} | |
} | |
}; | |
video.utils.Config = video.utils.Config || function (context, config) { | |
var _context = context; | |
var _config = config; | |
return function (property) { | |
/// <summary> | |
/// 1: config() - Gets the entire configuration object. | |
/// 2: config(property) - Gets the value of the given configuration property. | |
/// </summary> | |
/// <param name="property" optional="true" type="String">The property to get the configuration value of such as “outputLocation” or “video.filter.id” to traverse through multiple levels</param> | |
/// <returns type="Object">Configuration value is returned if property param is not null otherwise the entire configuration object is returned</returns> | |
if (typeof (property) != "undefined") { | |
var prop = null; | |
if (_config.hasOwnProperty(property)) { | |
prop = _config[property]; | |
} | |
else if (property.split('.').length > 1) { | |
var p = property.split('.'); | |
if (_config.hasOwnProperty(p[0])) { | |
prop = _config[p[0]]; | |
for (var i = 1; i < p.length && prop != null; i++) { | |
prop = prop.hasOwnProperty(p[i]) ? prop[p[i]] : null; | |
} | |
} | |
} | |
// The property was not found in the configuration, log a warning | |
if (prop == null && _context.logger != null) { | |
_context.logger.debug("'" + property + "' is null or was not found in the configuration object"); | |
} | |
return prop; | |
} else { | |
// Return the entire configration object | |
return _config; | |
} | |
}; | |
}; | |
video.utils.Logger = video.utils.Logger || function (config) { | |
var _config, _name, _enabled; | |
var getDate = function () { | |
var currentDate = new Date(); | |
return currentDate.getHours() + ":" + currentDate.getMinutes() + ":" + currentDate.getSeconds() + "." + currentDate.getMilliseconds(); | |
}; | |
this.log = function (msg, obj) { | |
if (_enabled) { | |
if (typeof (obj) != "undefined") { | |
console.log(getDate() + " |", _name, msg + " |", obj); | |
} else { | |
console.log(getDate() + " |", _name, msg); | |
} | |
} | |
}; | |
this.debug = function (msg, obj) { | |
if (_enabled) { | |
if (console.debug != null) { | |
if (typeof (obj) != "undefined") { | |
console.debug(getDate() + " |", _name, msg + " |", obj); | |
} else { | |
console.debug(getDate() + " |", _name, msg); | |
} | |
} else { | |
// The browser (IE) does not support debug, log it instead | |
this.log(msg, obj); | |
} | |
} | |
}; | |
this.info = function (msg, obj) { | |
if (_enabled) { | |
if (typeof (obj) != "undefined") { | |
console.info(getDate() + " |", _name, msg + " |", obj); | |
} else { | |
console.info(getDate() + " |", _name, msg); | |
} | |
} | |
}; | |
this.warn = function (msg, obj) { | |
if (_enabled) { | |
if (typeof (obj) != "undefined") { | |
console.warn(getDate() + " |", _name, msg + " |", obj); | |
} else { | |
console.warn(getDate() + " |", _name, msg); | |
} | |
} | |
}; | |
this.error = function (msg, obj) { | |
if (_enabled) { | |
if (typeof (obj) != "undefined") { | |
console.error(getDate() + " |", _name, msg + " |", obj); | |
} else { | |
console.error(getDate() + " |", _name, msg); | |
} | |
} | |
}; | |
(function () { | |
_config = $.extend( | |
true, | |
{ | |
// default settings | |
enabled: false, | |
name: "" | |
}, | |
config); // the configuration of the logger | |
// does console exist? if it doesn't, turn logging off | |
if (typeof (console) == "undefined") { | |
_config["enabled"] = false; | |
} else if (!_config["enabled"]) { | |
// Has a query string param override been provided? (debugvideo = true) | |
var queryString = location.href; | |
queryString = queryString.substr(queryString.indexOf('?') + 1); // remove everything before the ? | |
queryString = queryString.split('&'); | |
for (var i = 0; i < queryString.length; i++) { | |
var param = queryString[i].split('='); | |
if (param[0].toLowerCase() == "debugvideo") { | |
if (param[1].toLowerCase() == "true") { | |
_config["enabled"] = true; | |
} | |
} | |
} | |
} | |
_enabled = _config["enabled"]; | |
_name = ((_config["name"] != "") ? _config["name"] + " |" : ""); | |
})(); | |
}; | |
video.utils.sbk = video.utils.sbk = { | |
/// <summary>Tools used by SBK sites to help make the transition to the new framework easier</summary> | |
createTrackingObject: function () { | |
/// <summary>Creates a new ninemsn.portal.common.video.player.tracking object useing the settings supplied on the SBK site</summary> | |
var _category = "unknown"; | |
var _site = "unknown"; | |
var _section = "unknown"; | |
if (typeof (window.ninemsn.thirdParty) != "undefined") { | |
// Get the values from the new 3rd party header kit | |
_category = window.ninemsn.thirdParty.ThirdPartyModule.getOmnitureCategory(); | |
_site = window.ninemsn.thirdParty.ThirdPartyModuleConfig.getSiteName(); | |
_section = window.ninemsn.thirdParty.ThirdPartyModuleConfig.getSectionName(); | |
} | |
else { | |
// Get the values from the site settings | |
if (typeof (window._nmsnOCategory) != "undefined" && window._nmsnOCategory.search("msnportalaucat") == 0) { | |
window.JS_OMNTR_CATEGORY = window._nmsnOCategory.split(',')[0].substr(14); | |
} | |
if (typeof (window.JS_OMNTR_CATEGORY) != 'undefined' && window.JS_OMNTR_CATEGORY != '') { | |
_category = JS_OMNTR_CATEGORY; | |
} | |
if (typeof (window.JS_OMNTR_SITE) != "undefined" && window.JS_OMNTR_SITE != "") { | |
_site = window.JS_OMNTR_SITE; | |
} | |
else if (typeof (window.JS_SITE) != "undefined" && window.JS_SITE != "") { | |
_site = window.JS_SITE; | |
} | |
else if (typeof (window._nmsnSite) != "undefined" && window._nmsnSite != "") { | |
_site = window._nmsnSite; | |
} | |
if (typeof (window.JS_OMNTR_VIDEO_SECTION) != "undefined" && window.JS_OMNTR_VIDEO_SECTION != "") { | |
_section = window.JS_OMNTR_VIDEO_SECTION; | |
} | |
else if (typeof (window.JS_OMNTR_SECTION) != "undefined" && window.JS_OMNTR_SECTION != "") { | |
_section = window.JS_OMNTR_SECTION; | |
} | |
else if (typeof (window.JS_SECTION) != "undefined" && window.JS_SECTION != "") { | |
_section = window.JS_SECTION; | |
} | |
else if (typeof (window._nmsnSection) != "undefined" && window._nmsnSection != "") { | |
_section = window._nmsnSection; | |
} | |
} | |
return { | |
category: _category, | |
site: _site, | |
section: _section, | |
//subSection: "", | |
hierarchy: [_category, _site, _section] | |
}; | |
} | |
}; | |
video.utils.msn = video.utils.msn || { | |
getAccountFromMarket: function (mkt) { | |
var _translations = { | |
market: { | |
"en-au": video.enumerations.account.ninemsn, | |
"en-nz": video.enumerations.account.msnNZ, | |
"alt2-en-au": video.enumerations.account.ninemsnCatchup, | |
"alt-en-au": video.enumerations.account.thirdParty | |
} | |
}; | |
return _translations.market.hasOwnProperty(mkt) ? _translations.market[mkt] : null; | |
}, | |
getMarketFromAccount: function (acc) { | |
var _translations = { | |
account: { | |
ninemsn: "en-au", | |
msnNZ: "en-nz", | |
ninemsnCatchup: "alt2-en-au", | |
thirdParty: "alt-en-au" | |
} | |
}; | |
return _translations.account.hasOwnProperty(acc) ? _translations.account[acc] : null; | |
} | |
}; | |
video.utils.GetTargetString = video.utils.GetTargetString || function () { | |
var radCallUrl = 'http://rad.msn.com/ADSAdClient31.dll?GetAd=&PG=MSVFOO'; | |
var result = { ads: { freewheel: { keyValues: ''}} }; | |
var dfd = $.Deferred(); | |
var parseString = function(inputString, keyToFind, separator) { | |
if (inputString != null) { | |
var c = inputString.toLowerCase().indexOf(keyToFind.toLowerCase() + "="); | |
if (c != -1) { | |
c += keyToFind.length + 1; | |
var b = inputString.indexOf(separator, c); | |
b = b == -1 ? inputString.length : b; | |
return inputString.substring(c, b); | |
} | |
} | |
return ""; | |
}, | |
getParameterString = function() { | |
var cookie = document.cookie, | |
mhValue = escape(parseString(cookie, "mh", ";")), | |
parameterString = mhValue != "" ? "&PN=" + mhValue : "", | |
anonValue = escape(parseString(parseString(cookie, "ANON", ";"), "A", "&")), | |
muidValue = escape(parseString(cookie, "MUID", ";")); | |
if (anonValue == "") anonValue = muidValue; | |
parameterString += anonValue != "" ? "&ID=" + anonValue : ""; | |
parameterString += muidValue != "" ? "&MUID=" + muidValue : ""; | |
return parameterString; | |
}; | |
// Reject after 4 seconds, and return empty obj | |
setTimeout(function () { dfd.reject({}); }, 4000); | |
// Resolve ajax get rad call content | |
$.get(radCallUrl + getParameterString(), function (data) { | |
result.ads.freewheel.keyValues = '_fw_ekv=' + '1:' + data.substring(4); | |
dfd.resolve(result); | |
}); | |
return dfd.promise(); | |
}; | |
video.Widget = video.Widget || function (widgetConfig) { | |
/// <summary>Creates a new ninemsn.portal.common.video.widget object</summary> | |
/// <param name="config" optional="true" type="Object"></param> | |
/// <returns type="ninemsn.portal.common.video.widget" /> | |
// Ensure we are working with an instance of the object | |
var _context = (this instanceof video.Widget) ? this : new video.Widget(widgetConfig); | |
// the widget as a jQuery object, all events are attached to this | |
var _widget; | |
// stores the widget adapter object | |
var _adapter; | |
// stores the config object | |
var _config; | |
_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 }); | |
// 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 (_widget.on) != "undefined") { | |
_widget.on(event, handler); | |
} else { | |
_widget.bind(event, handler); | |
} | |
}; | |
_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 }); | |
// Executes once and then removes itself | |
_widget.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 }); | |
// 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 (_widget.off) != "undefined") { | |
_widget.off(event, handler); | |
} else { | |
_widget.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> | |
_context.logger.log(event, data); | |
_widget.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.send != null) { | |
_adapter.send(type, value); | |
} else { | |
_context.logger.warn(_context.config("type") + " 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.get != null) { | |
return _adapter.get(property); | |
} else { | |
_context.logger.warn(_context.config("type") + " 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.dispose != null) { | |
_adapter.dispose(); | |
} | |
_widget.empty(); | |
// Remove from the list of object | |
delete video.objects[_config["outputLocation"]]; | |
_widget = null; | |
_adapter = null; | |
_config = null; | |
_context = null; | |
}; | |
(function () { | |
// Merge the default settings with the supplied configuration | |
_config = $.extend(true, _config, widgetConfig); | |
// 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.Widget | " + _context.config("outputLocation"); | |
})() | |
}, | |
_context.config("logger") | |
) | |
); | |
// Get the player as a jQuery object | |
_widget = $("#" + _context.config("outputLocation")); | |
// init the widget | |
if (_widgets.hasOwnProperty(_config["type"])) { | |
_adapter = new _widgets[_config["type"]](_context); | |
} else { | |
_context.logger.error("'" + _config["type"] + "' is an unknown Widget type"); | |
} | |
// add it to the list of video objects on the page | |
video.objects[_config["outputLocation"]] = _context; | |
})(); | |
}; | |
var _widgets = _widgets || { | |
/// <summary>Namespace for nineemsn video widdgets</summary> | |
}; | |
_widgets[video.enumerations.widget.type.list] = _widgets[video.enumerations.widget.type.list] || function (widgetContext) { | |
var _context = widgetContext; | |
var _config = $.extend( | |
true, | |
{ | |
// default settings | |
baseURL: "/videos/?videoid=", | |
target: "_self", | |
descriptionLength: 35, | |
image: { width: 128, height: 72, defaultSrc: "http://shared.9msn.com.au/share/img/videoindex/thumb_default_AU.jpg" } | |
}, | |
_context.config("list")); // the configuration of the widget | |
var _data, _dataAdapter; | |
var render = function () { | |
var output = "<ul class=\"videoList\">\n"; | |
for (var i = 0; i < _data.videos.length; i++) { | |
var videoItem = _data.videos[i]; | |
if (videoItem.hasOwnProperty("image")) { | |
videoItem.image = videoItem.image(_config["image"].width, _config["image"].height); | |
} else { | |
videoItem.image = _config["image"].defaultSrc; | |
} | |
// Build the video link | |
var videoLink = _config["baseURL"] + videoItem.id; | |
// Append the src param to the video link | |
videoLink += (videoLink.indexOf('?') == -1) ? '?' : '&'; | |
videoLink += "src=widget:list"; | |
output += "<li id=\"" + _context.config("outputLocation") + "_" + videoItem.id + "\">" + | |
"<a href=\"" + videoLink + "\" title='" + videoItem.title + "' target='" + _config["target"] + "'>" + | |
"<img src=\"" + videoItem.image + "\" style=\"width: " + _config["image"].width + "px; height: " + _config["image"].height + "px\" />" + | |
"<p>" + | |
((videoItem.title.length > _config["descriptionLength"]) ? videoItem.title.substr(0, _config["descriptionLength"] - 3) + "..." : videoItem.title.substr(0, _config["descriptionLength"])) + | |
"</p>" + | |
"</a>" + | |
"</li>\n"; | |
} | |
output += "</ul>\n"; | |
$("#" + _context.config("outputLocation")).html(output); | |
// Raise the loaded event | |
_context.trigger("widget:loaded"); | |
}; | |
(function () { | |
// Raise the loading event | |
_context.trigger("widget:loading"); | |
// Get the data and display the list | |
if (_context.config("data") instanceof Array) { | |
// The data has been supplied, render the list using this information | |
_data = _context.config("data"); | |
render(); | |
} else { | |
// Get the data using the supplied settings | |
_dataAdapter = new video.Data( | |
$.extend( | |
true, | |
{ | |
success: function (data) { | |
_data = data; | |
render(); | |
} | |
}, | |
_context.config("data") | |
) | |
); | |
} | |
})(); | |
}; | |
_widgets[video.enumerations.widget.type.leadWithImage] = _widgets[video.enumerations.widget.type.leadWithImage] || function (widgetContext) { | |
var _context = widgetContext; | |
var _config = $.extend( | |
true, | |
{ | |
// default settings | |
descriptionLength: 220, | |
displayMetaData: true, | |
displayPlayButton: true, | |
createPlayerOnLoad: true, | |
width: 640, | |
height: 360, | |
strings: { | |
clickToPlay: "Click to Play Video" | |
} | |
}, | |
_context.config("leadWithImage")); // the configuration of the widget | |
var _data, _dataAdapter, _videoPlayer; | |
// Render the widget | |
var render = function () { | |
// Get the first item only (incase multiple were returned) | |
if (_data.videos instanceof Array) { | |
_data = _data.videos[0]; | |
} | |
// Inherit the width/height | |
_config["image"] = _config["image"] || {}; | |
_config.image["width"] = _config.image["width"] || _config["width"]; | |
_config.image["height"] = _config.image["height"] || _config["width"]; | |
// / Inherit the width/height | |
// Set the image width/height | |
var image = _data.image(_config["image"].width, _config["image"].height); | |
var output = "<div class=\"video_widget_leadWithImage\" style=\"background-image: url('" + image + "'); width: " + _config["width"] + "px; height: " + _config["height"] + "px;\" title=\"" + _config.strings["clickToPlay"] + "\">\n"; | |
if (_config["displayPlayButton"]) { | |
output += "<div class=\"play\"></div>\n"; | |
} | |
if (_config["displayMetaData"]) { | |
output += "<div class=\"metaBackground\"></div>\n" + | |
"<div class=\"meta\">\n" + | |
"<h2>" + _data.title + "</h2>\n" + | |
(_config["descriptionLength"] > 0 ? | |
("<p>" + ((_data.description.length > _config["descriptionLength"]) ? _data.description.substr(0, _config["descriptionLength"] - 3) + "..." : _data.description.substr(0, _config["descriptionLength"])) + "</p>\n") | |
: "") + | |
"</div>\n"; | |
} | |
output += "</div>\n"; | |
// Attach the click event | |
$("#" + _context.config("outputLocation")).html(output).find("div.video_widget_leadWithImage").click(function () { video.objects[_context.config("outputLocation")].send('PLAY'); }); | |
// Create the player | |
_videoPlayer = _context.config("player"); | |
if (_config["createPlayerOnLoad"]) { | |
createPlayer(); | |
} | |
// Ensure the widget is visible | |
$("#" + _context.config("outputLocation")).show(); | |
// Raise the loaded event | |
_context.trigger("widget:loaded"); | |
}; | |
var createPlayer = function () { | |
if ((_videoPlayer == null) || !(_videoPlayer instanceof video.Player)) { | |
// Render the player output location | |
var playerOutputLocation = _context.config("outputLocation") + "_player"; | |
if ($("#" + playerOutputLocation).length == 0) { | |
var output = "<div id=\"" + playerOutputLocation + "\"></div>"; | |
$(output).insertAfter("#" + _context.config("outputLocation")); | |
} | |
if (_videoPlayer == null) { | |
// Use the widget's data object | |
_videoPlayer = { | |
width: _config["width"], | |
height: _config["height"], | |
data: _context.config("data") | |
}; | |
} | |
// Create a new player | |
// set the output location to the widgets output location (with a _player suffix) | |
_videoPlayer.outputLocation = _context.config("outputLocation") + "_player"; | |
// Render the player | |
_videoPlayer.autoPlay = false; // ensure the video does not start playing in the background | |
_videoPlayer = new video.Player(_videoPlayer); | |
$("#" + _context.config("outputLocation") + "_player").width(0).height(0); | |
// Raise a player:created event, allowing other the events to be bound | |
_context.trigger("player:created", _videoPlayer); | |
} | |
}; | |
var showPlayer = function () { | |
$("#" + _context.config("outputLocation")).hide(); // Hide the lead with image widget | |
// Show the player | |
$("#" + _videoPlayer.config("outputLocation")).width(_videoPlayer.config("width")).height(_videoPlayer.config("height")); | |
// Play the video | |
if (_videoPlayer.get("STATUS") == "video:loaded") { | |
// The video is ready, play the video | |
_videoPlayer.send("PLAY"); | |
} else { | |
// the video isn't ready yet, when is it ready play the video | |
_videoPlayer.one("video:loaded", function () { | |
_videoPlayer.send("PLAY"); | |
}); | |
} | |
}; | |
this.send = function (type, param) { | |
var types = { | |
PLAY: function () { | |
if (_videoPlayer instanceof video.Player) { | |
// Show the player | |
showPlayer(); | |
} else { | |
// Attach an event tp show the player (once it has been created) | |
_context.on("player:created", showPlayer); | |
// Create the player | |
createPlayer(); | |
} | |
} | |
}; | |
if (types.hasOwnProperty(type)) { | |
types[type](param); | |
} | |
}; | |
this.dispose = function () { | |
if (_videoPlayer != null) { | |
_videoPlayer.dispose(); | |
} | |
if (_dataAdapter != null) { | |
_dataAdapter.dispose(); | |
} | |
}; | |
(function () { | |
// Raise the loading event | |
_context.trigger("widget:loading"); | |
// Get the data and display the list | |
if (_context.config("data.videos") != null && _context.config("data.videos") instanceof Array) { | |
// The data has been supplied, render the list using this information | |
_data = _context.config("data"); | |
render(); | |
} else { | |
// Get the data using the supplied settings | |
_dataAdapter = new video.Data( | |
$.extend( | |
true, | |
{ | |
success: function (data) { | |
_data = data; | |
render(); | |
} | |
}, | |
_context.config("data") | |
) | |
); | |
} | |
})(); | |
}; | |
_widgets[video.enumerations.widget.type.playlist] = _widgets[video.enumerations.widget.type.playlist] || function (widgetContext) { | |
var _context = widgetContext; | |
var _config = $.extend( | |
true, | |
{ | |
// default settings | |
addDefaultVideo: true, | |
removeDuplicatedVideos: true, | |
titleLength: 35, | |
scrollSpeed: 200, | |
animate: { enabled: true, properties: { opacity: "show" }, duration: "slow" }, | |
iScroll: { | |
enabled: (navigator.userAgent.toLowerCase().match(/(iphone|ipod|ipad|android)/) != null), | |
config: { | |
hScrollbar: false, vScrollbar: false, vScroll: false, momentum: false | |
} | |
}, | |
image: { width: 128, height: 72, defaultSrc: "http://shared.9msn.com.au/share/img/videoindex/thumb_default_AU.jpg" }, | |
moreVideos: { enabled: true, link: "/video", target: "_self" }, | |
strings: { | |
playing: "Currently Viewing", | |
next: "Up Next", | |
moreVideos: "Continue viewing other video clips from this site", | |
arrowLeft: "Scroll Left", | |
arrowRight: "Scroll Right" | |
} | |
}, | |
_context.config("playlist")); // the configuration of the widget | |
var _data, _dataAdapter, _scroller, _playlistWidth, _itemWidth, _viewableItems; | |
var _outputLocation = _context.config("outputLocation"); | |
var highlight = function (id) { | |
// Remove the selected class from all related videos | |
$("#" + _outputLocation + "_playlist li").removeClass("selected"); | |
// Hide the playing/up next messages | |
$("#" + _outputLocation + "_playlist li span").text("").hide(); | |
// Highlight the video | |
$("#" + _outputLocation + "_playlist_" + id).addClass("selected"); | |
$("#" + _outputLocation + "_playlist_" + id + " span").text(_config["strings"].playing).css("display", "block"); | |
// Add the "up next" message to the next item | |
var nextVideo = $("#" + _outputLocation + "_playlist_" + id).next(); | |
nextVideo = (nextVideo.length > 0) ? nextVideo : $("#" + _outputLocation + "_playlist li:first"); | |
nextVideo = nextVideo.find("span"); | |
nextVideo.text(_config["strings"].next).css("display", "block"); | |
if (_scroller != null) { | |
_scroller.scrollToElement("#" + _outputLocation + "_playlist_" + id, _config["scrollSpeed"]); | |
} else { | |
var videoOffsetLeft = ($("#" + _outputLocation + "_playlist_" + id).length == 1 ? $("#" + _outputLocation + "_playlist_" + id).offset().left : 0); | |
$("#" + _outputLocation + "_playlist").animate({ | |
scrollLeft: (videoOffsetLeft - $("#" + _outputLocation + "_playlist ul").offset().left) | |
}, _config["scrollSpeed"]); | |
} | |
}; | |
var loadVideo = function (id) { | |
// Loads a video into the player | |
var player = _context.config("player"); | |
// Stop the current video | |
player.send("STOP"); | |
// Load the next video | |
player.send("LOAD", id); | |
// Highlight the next video | |
highlight(id); | |
// The video won't start playing on it's own, start playing it | |
player.one("video:loaded", function () { | |
player.send("PLAY"); | |
}); | |
}; | |
var loadNext = function () { | |
// Get the id of the current video | |
var currentVideo = _context.config("player").get("META"); | |
// Get the id of the next video | |
var nextVideo; | |
for (var i = 0; i < _data.videos.length && nextVideo == null; i++) { | |
if (_data.videos[i].id == currentVideo.id) { | |
var nextVideoIndex = i + 1; | |
if (nextVideoIndex < _data.videos.length) { | |
nextVideo = _data.videos[nextVideoIndex]; | |
} | |
} | |
} | |
if (nextVideo == null) { | |
// Cannot find the next video, play the first video in the list | |
nextVideo = _data.videos[0]; | |
} | |
loadVideo(nextVideo.id); | |
}; | |
// Remove Duplicate videos | |
var removeDuplicates = function (videos) { | |
var output = []; | |
var duplicateFound; | |
for (var i = 0; i < videos.length; i++) { | |
duplicateFound = false; | |
for (var j = 0; j < output.length && !duplicateFound; j++) { | |
duplicateFound = (videos[i].id == output[j].id); | |
} | |
if (!duplicateFound) { | |
output.push(videos[i]); | |
} | |
} | |
return output; | |
}; | |
var render = function () { | |
// remove duplicated videos | |
if (_config["removeDuplicatedVideos"]) { | |
_data.videos = removeDuplicates(_data.videos); | |
} | |
var output = "<div class=\"video_widget_playlist\">\n" + | |
"<div id=\"leftArrow\" class=\"arrow\"></div>\n" + | |
"<div id=\"" + _outputLocation + "_playlist\" class=\"playlist\">\n" + | |
"<ul>\n"; | |
for (var i = 0; i < _data.videos.length; i++) { | |
var videoItem = _data.videos[i]; | |
if (videoItem.hasOwnProperty("image")) { | |
videoItem.image = videoItem.image(_config["image"].width, _config["image"].height); | |
} else { | |
videoItem.image = _config["image"].defaultSrc; | |
} | |
output += | |
"<li id=\"" + _outputLocation + "_playlist_" + videoItem.id + "\" class=\"videoListItem\">\n" + | |
"<div class=\"outer\">\n" + | |
"<div class=\"imgWrapper\">\n" + | |
"<img src=\"" + videoItem.image + "\" width=\"" + _config["image"].width + "\" height=\"" + _config["image"].height + "\" />\n" + | |
"<span></span>\n" + | |
"</div>\n" + | |
"<h3>" + videoItem.title + "</h3>\n" + | |
/*"<div class=\"description\">\n" + | |
"<p>" + | |
((video.title.length > _config["titleLength"]) ? video.title.substr(0, _config["titleLength"] - 3) + "..." : video.title.substr(0, _config["titleLength"])) + | |
"</p>\n" + | |
"<span class=\"videoDate\">" + video.date + "</span>\n" + | |
"<span class=\"videoSource\">News</span>\n" + | |
"</div>\n" +*/ | |
"</div>\n" + | |
"</li>\n"; | |
} | |
if (_config["moreVideos"].enabled) { | |
// Append the src param | |
_config["moreVideos"].link += (_config["moreVideos"].link.indexOf('?') == -1) ? '?' : '&'; | |
_config["moreVideos"].link += "src=widget:playlist"; | |
output += | |
"<li class=\"moreVideosListItem\">\n" + | |
"<div class=\"outer moreVideos\">\n" + | |
"<a href=\"" + _config["moreVideos"].link + "\" target=\"" + _config["moreVideos"].target + "\">" + | |
"<h3>" + _config["strings"].moreVideos + "</h3>\n" + | |
"</a\n" + | |
"</div>\n" + | |
"</li>\n"; | |
} | |
output += "</ul>\n" + | |
"</div>\n" + | |
"<div id=\"rightArrow\" class=\"arrow\"></div>\n" + | |
"</div>\n"; | |
// Render the list (animate if the browser is not IE or it is and is >= IE9) | |
if (_config["animate"] != null && _config["animate"].enabled && !($.browser.msie && $.browser.version < 9)) { | |
$("#" + _outputLocation).html(output).hide().animate(_config["animate"].properties, _config["animate"].duration); | |
} else { | |
$("#" + _outputLocation).html(output); | |
} | |
// Attach the click event to the right arrow | |
$("#" + _outputLocation).find("div#rightArrow").click(function () { | |
video.objects[_outputLocation].send('SCROLL', 'right'); | |
}); | |
// Attach the click event to the left arrow | |
$("#" + _outputLocation).find("div#leftArrow").click(function () { | |
video.objects[_outputLocation].send('SCROLL', 'left'); | |
}); | |
// Attach click events to all videos in the list | |
$("#" + _outputLocation).find("#" + _outputLocation + "_playlist li.videoListItem").each(function (index, element) { | |
var elementId = $(element).attr("id"); | |
if (elementId) { | |
var videoId = elementId.split("_").pop(); | |
$(element).click(function () { | |
video.objects[_outputLocation].send('PLAY', videoId); | |
}); | |
} | |
}); | |
// Set the width of the playlist | |
var arrowWidth = $("#" + _outputLocation + " .arrow").width(); | |
var wrapperWidth = $("#" + _outputLocation + "").width(); | |
_playlistWidth = ((wrapperWidth - (arrowWidth * 2)) - 20); | |
$("#" + _outputLocation + "_playlist").width(_playlistWidth); | |
_itemWidth = $("#" + _outputLocation + "_playlist ul:first li:first").outerWidth() + 2; | |
_viewableItems = parseInt(_playlistWidth / _itemWidth); | |
// Highlight the current video | |
highlight(_context.config("player").get("META").id); | |
// Set the width of the inside scroll | |
$("#" + _outputLocation + "_playlist ul:first").width( | |
($("#" + _outputLocation + "_playlist ul:first li:first").outerWidth() + 2) * $("#" + _outputLocation + "_playlist ul:first li").length | |
); | |
// Ensure iScroll is disabled in IE | |
if ($.browser.msie) { | |
_config["iScroll"].enabled = false; | |
_context.logger.warn("iScroll disabled. iScroll is not supported in IE"); | |
} else if (_config["iScroll"].enabled && typeof (iScroll) != "undefined") { | |
// ReSharper disable InconsistentNaming | |
// Name cannot be changes as it's a public library | |
_scroller = new window.iScroll(_outputLocation + "_playlist", _config["iScroll"].config); // attach the scroll handler | |
// ReSharper restore InconsistentNaming | |
_scroller.refresh(); | |
} | |
// Raise the loaded event | |
_context.trigger("widget:loaded"); | |
}; | |
this.send = function (type, param) { | |
var types = { | |
PLAY: function (id) { | |
loadVideo(id); | |
}, | |
SCROLL: function (direction) { | |
if (_scroller != null) { | |
if (direction.toLowerCase() == "left") { | |
_scroller.scrollTo(0 - (_viewableItems * _itemWidth), 0, _config["scrollSpeed"], true); | |
} else { | |
_scroller.scrollTo((_viewableItems * _itemWidth), 0, _config["scrollSpeed"], true); | |
} | |
_scroller.refresh(); | |
} else { | |
if (direction.toLowerCase() == "left") { | |
$("#" + _outputLocation + "_playlist").animate({ scrollLeft: (function () { return "-=" + (_viewableItems * _itemWidth); } ()) }, _config["scrollSpeed"]); | |
} else { | |
$("#" + _outputLocation + "_playlist").animate({ scrollLeft: (function () { return "+=" + (_viewableItems * _itemWidth); } ()) }, _config["scrollSpeed"]); | |
} | |
} | |
} | |
}; | |
if (types.hasOwnProperty(type)) { | |
types[type](param); | |
} | |
}; | |
this.dispose = function () { | |
if (_scroller != null) { | |
_scroller.destroy(); | |
_scroller = null; | |
} | |
}; | |
(function () { | |
// Raise the loading event | |
_context.trigger("widget:loading"); | |
// Attach to the player events | |
_context.config("player").on("video:playCompleted", loadNext); | |
// Get the data and display the list | |
if (_context.config("data") instanceof Array) { | |
// The data has been supplied, render the playlist | |
_data = _context.config("data"); | |
if (_config.addDefaultVideo) { | |
_data.unshift(_context.config("player").get("META")); | |
} | |
render(); | |
} else { | |
// Get the data using the supplied config | |
_dataAdapter = new video.Data( | |
$.extend( | |
true, | |
{ | |
success: function (data) { | |
if (_config.addDefaultVideo) { | |
data.videos.unshift(_context.config("player").get("META")); | |
} | |
_data = data; | |
render(); | |
}, | |
paging: { size: 20 }, | |
msn: { | |
fileFilter: (function () { | |
// if the player is in HTML mode only return videos that can be played in that format | |
return (_context.config("player").get("TYPE") != video.enumerations.player.type.HTML5) ? 80 : 800; | |
})() | |
} | |
}, | |
_context.config("data") | |
) | |
); | |
} | |
})(); | |
}; | |
_widgets[video.enumerations.widget.type.suggestedVideos] = _widgets[video.enumerations.widget.type.suggestedVideos] || | |
function (widgetContext) { | |
var _context = widgetContext; | |
var _config = $.extend( | |
true, | |
{ | |
// default settings | |
playVideo: { baseUrl: "/video/?videoid=", target: "_self" }, | |
moreVideos: { enabled: true, link: "/video/", target: "_self" }, | |
strings: { widgetTitle: "Videos You May Like", moreVideos: "+ More Video", playVideo: "Play Video" }, | |
commonWordFilter: [ | |
"the", "be", "to", "of", "and", "a", "in", "that", "have", "i", "it", "for", "not", "on", "with", "he", "as", "you", "do", "at", | |
"this", "but", "his", "by", "from", "they", "we", "say", "her", "she", "or", "an", "will", "my", "one", "all", "would", "there", | |
"their", "what", "so", "up", "out", "if", "about", "who", "get", "which", "go", "me", "when", "make", "can", "like", "time", "no", | |
"just", "him", "know", "take", "person", "into", "year", "your", "good", "some", "could", "them", "see", "other", "than", "then", | |
"now", "look", "only", "come", "its", "over", "think", "also", "back", "after", "use", "two", "how", "our", "work", "first", "well", | |
"way", "even", "new", "want", "because", "any", "these", "give", "day", "most", "us", "is", "didnt", "dont", "off", "where", "not", | |
"was", "ninemsn" | |
], | |
descriptionLength: 50, | |
image: { | |
width: 128, | |
height: 72, | |
defaultSrc: "http://shared.9msn.com.au/share/img/videoindex/thumb_default_AU.jpg" | |
} | |
}, | |
_context.config("suggestedVideos") | |
); // the configuration of the widget | |
// determine suggested videos | |
var filterArray = _config.commonWordFilter; | |
var _data, _dataAdapter; | |
// render the videos | |
var render = function () { | |
var output = "<div class=\"video_widget_suggestedVideos\">"; | |
output += "<hr />"; | |
output += "<h2>" + _config.strings.widgetTitle + "</h2>"; | |
if (_config.moreVideos.enabled) { | |
// Append the src param | |
_config.moreVideos.link += (_config.moreVideos.link.indexOf('?') === -1) ? '?' : '&'; | |
_config.moreVideos.link += "src=widget:suggestedVideos"; | |
output += "<span id=\"moreVideos\"><a class=\"more_link\" href=\"" + _config.moreVideos.link | |
+ "\" target=\"" + _config.moreVideos.target + "\">" + _config.strings.moreVideos + "</a></span>"; | |
} | |
output += "<ul>\n"; | |
var i; | |
for (i = 0; i < _data.videos.length; i += 1) { | |
var videoItem = _data.videos[i]; | |
if (videoItem.hasOwnProperty("image")) { | |
videoItem.image = videoItem.image(_config.image.width, _config.image.height); | |
} else { | |
videoItem.image = _config.image.defaultSrc; | |
} | |
// Build the video link | |
var videoLink = _config.playVideo.baseUrl + videoItem.id; | |
// Append the src param to the video link | |
videoLink += (videoLink.indexOf('?') === -1) ? '?' : '&'; | |
videoLink += "src=widget:suggestedVideos"; | |
output += "<li id=\"" + _context.config("outputLocation") + "_" + videoItem.id + "\">" + | |
"<div class=\"imageItem\">" + | |
"<a href=\"" + videoLink + "\" title=\"" + videoItem.title + "\" target='" + _config.playVideo.target | |
+ "'>" + | |
"<img src=\"" + videoItem.image + "\" />" + | |
"</a>" + | |
"<div class=\"overlay\"><a href=\"" + videoLink + "\" target='" + _config.playVideo.target + "'>" | |
+ _config.strings.playVideo + "</a></div>" + | |
"</div>" + | |
"<a href=\"" + videoLink + "\" class=\"videoText\" target='" + _config.playVideo.target + "'>" + | |
((videoItem.title.length > _config.descriptionLength) | |
? videoItem.title.substr(0, _config.descriptionLength - 3) + "..." | |
: videoItem.title.substr(0, _config.descriptionLength)) + | |
"</a>" + | |
"</li>\n"; | |
} | |
output += "</ul>\n"; | |
output += "<hr /></div>"; | |
$("#" + _context.config("outputLocation")).html(output); | |
// Raise the loaded event | |
_context.trigger("widget:loaded"); | |
}; | |
// determin if array has a particular value | |
var arrHasValue = function (arrayToFind, valueToCompare) { | |
var result = false; | |
var i; | |
for (i = 0; i < arrayToFind.length; i += 1) { | |
if (arrayToFind[i] === valueToCompare) { | |
result = true; | |
} | |
} | |
return result; | |
}; | |
// return unique element array from joining two arrays | |
var returnUnionFromArrs = function (arr1, arr2) { | |
var arr = []; | |
var x, i; | |
for (x = 0; x < arr2.length; x += 1) { | |
arr.push(arr2[x]); | |
} | |
for (i = 0; i < arr1.length; i += 1) { | |
if (!arrHasValue(arr, arr1[i])) { | |
arr.push(arr1[i]); | |
} | |
} | |
return arr; | |
}; | |
// return common element array from two arrays | |
var returnCommonFromArrs = function (arr1, arr2, union) { | |
union = (typeof union === "undefined") ? true : union; | |
var arr = []; | |
var i; | |
for (i = 0; i < arr1.length; i += 1) { | |
if (arrHasValue(arr2, arr1[i])) { | |
arr.push(arr1[i]); | |
} | |
} | |
if (arr.length < 2 && union) { | |
arr = returnUnionFromArrs(arr1, arr2); | |
} | |
return arr; | |
}; | |
// build array from string, filtered by word filter | |
var buildArrByString = function (arr, fullString) { | |
fullString = fullString.replace(/[,|.|;|!|:|'|-|\$]/g, ' '); | |
var tempTags = fullString.split(' '); | |
var k; | |
for (k = 0; k < tempTags.length; k += 1) { | |
if (tempTags[k].length > 0) { | |
if (!arrHasValue(arr, tempTags[k]) && tempTags[k].length > 1) { | |
arr.push(tempTags[k].toLowerCase()); | |
} | |
} | |
} | |
if (arr.length > 0) { | |
removeItems(arr, filterArray); | |
} | |
return arr; | |
}; | |
// get the keyword | |
var getPageKeyword = function () { | |
var tagArray = [], | |
titleArray = [], | |
keywordArray = [], | |
descriptionArray = [], | |
commonArray1, | |
commonArray2, | |
commonArray3, | |
unionArray; | |
var tagVar = "", tagArrVar, match, metadata, result = ""; | |
// Get ninemsn sbk Tags | |
var j, y; | |
for (j = 1; j <= 10; j += 1) { | |
if (typeof (window['JS_TAGGING_TAGS_' + j]) !== "undefined") { | |
tagArrVar = window['JS_TAGGING_TAGS_' + j].split("~"); | |
for (y = 0; y < tagArrVar.length; y += 1) { | |
match = tagArrVar[y].split("|"); | |
if (typeof match[1] !== "undefined") { | |
tagVar += match[1] + " "; | |
} | |
} | |
} | |
} | |
tagArray = buildArrByString(tagArray, tagVar); | |
// Get Title Array | |
titleArray = buildArrByString(titleArray, $("title").html()); | |
// Get Keywords/discription array | |
metadata = document.getElementsByTagName("meta"); | |
var i; | |
for (i = 0; i < metadata.length; i += 1) { | |
switch (metadata[i].name) { | |
case 'keywords': | |
keywordArray = buildArrByString(keywordArray, metadata[i].content); | |
break; | |
case 'description': | |
descriptionArray = buildArrByString(descriptionArray, metadata[i].content); | |
break; | |
} | |
} | |
commonArray1 = returnCommonFromArrs(titleArray, keywordArray, (tagArray.length === 0)); | |
commonArray2 = returnCommonFromArrs(titleArray, descriptionArray, false); | |
commonArray3 = returnCommonFromArrs(keywordArray, descriptionArray, false); | |
unionArray = returnUnionFromArrs( | |
returnUnionFromArrs( | |
returnUnionFromArrs(commonArray1, commonArray2), | |
returnUnionFromArrs(commonArray1, commonArray3) | |
), | |
tagArray | |
); | |
// Clear up union array to take down words less than 3 chars if total keywords larger than 2 | |
for (i = unionArray.length - 1; i >= 0; i--) { | |
var item = unionArray[i]; | |
if (unionArray.length > 2 && item.length < 3) { | |
removeItems(unionArray, item); | |
} | |
} | |
var r; | |
for (r = 0; r < unionArray.length; r += 1) { | |
result += (result === "") ? unionArray[r] : " " + unionArray[r]; | |
} | |
// Limit query to be less than 100 chars | |
if (result.length > 100) { | |
result = result.substring(0, result.substring(0, 100 - 1).lastIndexOf(" ")); | |
} | |
return result; | |
}; | |
var removeItems = function (originalArray, itemsToRemove) { | |
if (!/Array/.test(itemsToRemove.constructor)) { | |
itemsToRemove = [itemsToRemove]; | |
} | |
var j, i; | |
for (i = 0; i < itemsToRemove.length; i += 1) { | |
j = 0; | |
while (j < originalArray.length) { | |
if (originalArray[j] === itemsToRemove[i]) { | |
originalArray.splice(j, 1); | |
} else { | |
j += 1; | |
} | |
} | |
} | |
}; | |
(function () { | |
// Raise the loading event | |
_context.trigger("widget:loading"); | |
// determine the suggested videos | |
_dataAdapter = new video.Data( | |
$.extend( | |
true, | |
{ | |
success: function (data) { | |
_data = data; | |
render(); | |
}, | |
method: video.enumerations.data.method.Search, | |
filter: { | |
term: getPageKeyword(), | |
operator: video.enumerations.data.searchOperator.Any | |
} | |
}, | |
_context.config("data") | |
) | |
); | |
} ()); | |
}; | |
/*! | |
* iScroll v4.1.9 ~ Copyright (c) 2011 Matteo Spinelli, http://cubiq.org | |
* Released under MIT license, http://cubiq.org/license | |
*/ | |
(function(){ | |
var m = Math, | |
mround = function (r) { return r >> 0; }, | |
vendor = (/webkit/i).test(navigator.appVersion) ? 'webkit' : | |
(/firefox/i).test(navigator.userAgent) ? 'Moz' : | |
'opera' in window ? 'O' : '', | |
// Browser capabilities | |
isAndroid = (/android/gi).test(navigator.appVersion), | |
isIDevice = (/iphone|ipad/gi).test(navigator.appVersion), | |
isPlaybook = (/playbook/gi).test(navigator.appVersion), | |
isTouchPad = (/hp-tablet/gi).test(navigator.appVersion), | |
has3d = 'WebKitCSSMatrix' in window && 'm11' in new WebKitCSSMatrix(), | |
hasTouch = 'ontouchstart' in window && !isTouchPad, | |
hasTransform = vendor + 'Transform' in document.documentElement.style, | |
hasTransitionEnd = isIDevice || isPlaybook, | |
nextFrame = (function() { | |
return window.requestAnimationFrame | |
|| window.webkitRequestAnimationFrame | |
|| window.mozRequestAnimationFrame | |
|| window.oRequestAnimationFrame | |
|| window.msRequestAnimationFrame | |
|| function(callback) { return setTimeout(callback, 1); } | |
})(), | |
cancelFrame = (function () { | |
return window.cancelRequestAnimationFrame | |
|| window.webkitCancelRequestAnimationFrame | |
|| window.mozCancelRequestAnimationFrame | |
|| window.oCancelRequestAnimationFrame | |
|| window.msCancelRequestAnimationFrame | |
|| clearTimeout | |
})(), | |
// Events | |
RESIZE_EV = 'onorientationchange' in window ? 'orientationchange' : 'resize', | |
START_EV = hasTouch ? 'touchstart' : 'mousedown', | |
MOVE_EV = hasTouch ? 'touchmove' : 'mousemove', | |
END_EV = hasTouch ? 'touchend' : 'mouseup', | |
CANCEL_EV = hasTouch ? 'touchcancel' : 'mouseup', | |
WHEEL_EV = vendor == 'Moz' ? 'DOMMouseScroll' : 'mousewheel', | |
// Helpers | |
trnOpen = 'translate' + (has3d ? '3d(' : '('), | |
trnClose = has3d ? ',0)' : ')', | |
// Constructor | |
iScroll = function (el, options) { | |
var that = this, | |
doc = document, | |
i; | |
that.wrapper = typeof el == 'object' ? el : doc.getElementById(el); | |
that.wrapper.style.overflow = 'hidden'; | |
that.scroller = that.wrapper.children[0]; | |
// Default options | |
that.options = { | |
hScroll: true, | |
vScroll: true, | |
x: 0, | |
y: 0, | |
bounce: true, | |
bounceLock: false, | |
momentum: true, | |
lockDirection: true, | |
useTransform: true, | |
useTransition: false, | |
topOffset: 0, | |
checkDOMChanges: false, // Experimental | |
// Scrollbar | |
hScrollbar: true, | |
vScrollbar: true, | |
fixedScrollbar: isAndroid, | |
hideScrollbar: isIDevice, | |
fadeScrollbar: isIDevice && has3d, | |
scrollbarClass: '', | |
// Zoom | |
zoom: false, | |
zoomMin: 1, | |
zoomMax: 4, | |
doubleTapZoom: 2, | |
wheelAction: 'scroll', | |
// Snap | |
snap: false, | |
snapThreshold: 1, | |
// Events | |
onRefresh: null, | |
onBeforeScrollStart: function (e) { e.preventDefault(); }, | |
onScrollStart: null, | |
onBeforeScrollMove: null, | |
onScrollMove: null, | |
onBeforeScrollEnd: null, | |
onScrollEnd: null, | |
onTouchEnd: null, | |
onDestroy: null, | |
onZoomStart: null, | |
onZoom: null, | |
onZoomEnd: null | |
}; | |
// Helpers FIX ANDROID BUG! | |
// translate3d and scale doesn't work together! | |
// Ignoring 3d ONLY WHEN YOU SET that.zoom | |
if ( that.zoom && isAndroid ){ | |
trnOpen = 'translate('; | |
trnClose = ')'; | |
} | |
// User defined options | |
for (i in options) that.options[i] = options[i]; | |
// Set starting position | |
that.x = that.options.x; | |
that.y = that.options.y; | |
// Normalize options | |
that.options.useTransform = hasTransform ? that.options.useTransform : false; | |
that.options.hScrollbar = that.options.hScroll && that.options.hScrollbar; | |
that.options.vScrollbar = that.options.vScroll && that.options.vScrollbar; | |
that.options.zoom = that.options.useTransform && that.options.zoom; | |
that.options.useTransition = hasTransitionEnd && that.options.useTransition; | |
// Set some default styles | |
that.scroller.style[vendor + 'TransitionProperty'] = that.options.useTransform ? '-' + vendor.toLowerCase() + '-transform' : 'top left'; | |
that.scroller.style[vendor + 'TransitionDuration'] = '0'; | |
that.scroller.style[vendor + 'TransformOrigin'] = '0 0'; | |
if (that.options.useTransition) that.scroller.style[vendor + 'TransitionTimingFunction'] = 'cubic-bezier(0.33,0.66,0.66,1)'; | |
if (that.options.useTransform) that.scroller.style[vendor + 'Transform'] = trnOpen + that.x + 'px,' + that.y + 'px' + trnClose; | |
else that.scroller.style.cssText += ';position:absolute;top:' + that.y + 'px;left:' + that.x + 'px'; | |
if (that.options.useTransition) that.options.fixedScrollbar = true; | |
that.refresh(); | |
that._bind(RESIZE_EV, window); | |
that._bind(START_EV); | |
if (!hasTouch) { | |
that._bind('mouseout', that.wrapper); | |
if (that.options.wheelAction != 'none') | |
that._bind(WHEEL_EV); | |
} | |
if (that.options.checkDOMChanges) that.checkDOMTime = setInterval(function () { | |
that._checkDOMChanges(); | |
}, 500); | |
}; | |
// Prototype | |
iScroll.prototype = { | |
enabled: true, | |
x: 0, | |
y: 0, | |
steps: [], | |
scale: 1, | |
currPageX: 0, currPageY: 0, | |
pagesX: [], pagesY: [], | |
aniTime: null, | |
wheelZoomCount: 0, | |
handleEvent: function (e) { | |
var that = this; | |
switch(e.type) { | |
case START_EV: | |
if (!hasTouch && e.button !== 0) return; | |
that._start(e); | |
break; | |
case MOVE_EV: that._move(e); break; | |
case END_EV: | |
case CANCEL_EV: that._end(e); break; | |
case RESIZE_EV: that._resize(); break; | |
case WHEEL_EV: that._wheel(e); break; | |
case 'mouseout': that._mouseout(e); break; | |
case 'webkitTransitionEnd': that._transitionEnd(e); break; | |
} | |
}, | |
_checkDOMChanges: function () { | |
if (this.moved || this.zoomed || this.animating || | |
(this.scrollerW == this.scroller.offsetWidth * this.scale && this.scrollerH == this.scroller.offsetHeight * this.scale)) return; | |
this.refresh(); | |
}, | |
_scrollbar: function (dir) { | |
var that = this, | |
doc = document, | |
bar; | |
if (!that[dir + 'Scrollbar']) { | |
if (that[dir + 'ScrollbarWrapper']) { | |
if (hasTransform) that[dir + 'ScrollbarIndicator'].style[vendor + 'Transform'] = ''; | |
that[dir + 'ScrollbarWrapper'].parentNode.removeChild(that[dir + 'ScrollbarWrapper']); | |
that[dir + 'ScrollbarWrapper'] = null; | |
that[dir + 'ScrollbarIndicator'] = null; | |
} | |
return; | |
} | |
if (!that[dir + 'ScrollbarWrapper']) { | |
// Create the scrollbar wrapper | |
bar = doc.createElement('div'); | |
if (that.options.scrollbarClass) bar.className = that.options.scrollbarClass + dir.toUpperCase(); | |
else bar.style.cssText = 'position:absolute;z-index:100;' + (dir == 'h' ? 'height:7px;bottom:1px;left:2px;right:' + (that.vScrollbar ? '7' : '2') + 'px' : 'width:7px;bottom:' + (that.hScrollbar ? '7' : '2') + 'px;top:2px;right:1px'); | |
bar.style.cssText += ';pointer-events:none;-' + vendor + '-transition-property:opacity;-' + vendor + '-transition-duration:' + (that.options.fadeScrollbar ? '350ms' : '0') + ';overflow:hidden;opacity:' + (that.options.hideScrollbar ? '0' : '1'); | |
that.wrapper.appendChild(bar); | |
that[dir + 'ScrollbarWrapper'] = bar; | |
// Create the scrollbar indicator | |
bar = doc.createElement('div'); | |
if (!that.options.scrollbarClass) { | |
bar.style.cssText = 'position:absolute;z-index:100;background:rgba(0,0,0,0.5);border:1px solid rgba(255,255,255,0.9);-' + vendor + '-background-clip:padding-box;-' + vendor + '-box-sizing:border-box;' + (dir == 'h' ? 'height:100%' : 'width:100%') + ';-' + vendor + '-border-radius:3px;border-radius:3px'; | |
} | |
bar.style.cssText += ';pointer-events:none;-' + vendor + '-transition-property:-' + vendor + '-transform;-' + vendor + '-transition-timing-function:cubic-bezier(0.33,0.66,0.66,1);-' + vendor + '-transition-duration:0;-' + vendor + '-transform:' + trnOpen + '0,0' + trnClose; | |
if (that.options.useTransition) bar.style.cssText += ';-' + vendor + '-transition-timing-function:cubic-bezier(0.33,0.66,0.66,1)'; | |
that[dir + 'ScrollbarWrapper'].appendChild(bar); | |
that[dir + 'ScrollbarIndicator'] = bar; | |
} | |
if (dir == 'h') { | |
that.hScrollbarSize = that.hScrollbarWrapper.clientWidth; | |
that.hScrollbarIndicatorSize = m.max(mround(that.hScrollbarSize * that.hScrollbarSize / that.scrollerW), 8); | |
that.hScrollbarIndicator.style.width = that.hScrollbarIndicatorSize + 'px'; | |
that.hScrollbarMaxScroll = that.hScrollbarSize - that.hScrollbarIndicatorSize; | |
that.hScrollbarProp = that.hScrollbarMaxScroll / that.maxScrollX; | |
} else { | |
that.vScrollbarSize = that.vScrollbarWrapper.clientHeight; | |
that.vScrollbarIndicatorSize = m.max(mround(that.vScrollbarSize * that.vScrollbarSize / that.scrollerH), 8); | |
that.vScrollbarIndicator.style.height = that.vScrollbarIndicatorSize + 'px'; | |
that.vScrollbarMaxScroll = that.vScrollbarSize - that.vScrollbarIndicatorSize; | |
that.vScrollbarProp = that.vScrollbarMaxScroll / that.maxScrollY; | |
} | |
// Reset position | |
that._scrollbarPos(dir, true); | |
}, | |
_resize: function () { | |
var that = this; | |
setTimeout(function () { that.refresh(); }, isAndroid ? 200 : 0); | |
}, | |
_pos: function (x, y) { | |
x = this.hScroll ? x : 0; | |
y = this.vScroll ? y : 0; | |
if (this.options.useTransform) { | |
this.scroller.style[vendor + 'Transform'] = trnOpen + x + 'px,' + y + 'px' + trnClose + ' scale(' + this.scale + ')'; | |
} else { | |
x = mround(x); | |
y = mround(y); | |
this.scroller.style.left = x + 'px'; | |
this.scroller.style.top = y + 'px'; | |
} | |
this.x = x; | |
this.y = y; | |
this._scrollbarPos('h'); | |
this._scrollbarPos('v'); | |
}, | |
_scrollbarPos: function (dir, hidden) { | |
var that = this, | |
pos = dir == 'h' ? that.x : that.y, | |
size; | |
if (!that[dir + 'Scrollbar']) return; | |
pos = that[dir + 'ScrollbarProp'] * pos; | |
if (pos < 0) { | |
if (!that.options.fixedScrollbar) { | |
size = that[dir + 'ScrollbarIndicatorSize'] + mround(pos * 3); | |
if (size < 8) size = 8; | |
that[dir + 'ScrollbarIndicator'].style[dir == 'h' ? 'width' : 'height'] = size + 'px'; | |
} | |
pos = 0; | |
} else if (pos > that[dir + 'ScrollbarMaxScroll']) { | |
if (!that.options.fixedScrollbar) { | |
size = that[dir + 'ScrollbarIndicatorSize'] - mround((pos - that[dir + 'ScrollbarMaxScroll']) * 3); | |
if (size < 8) size = 8; | |
that[dir + 'ScrollbarIndicator'].style[dir == 'h' ? 'width' : 'height'] = size + 'px'; | |
pos = that[dir + 'ScrollbarMaxScroll'] + (that[dir + 'ScrollbarIndicatorSize'] - size); | |
} else { | |
pos = that[dir + 'ScrollbarMaxScroll']; | |
} | |
} | |
that[dir + 'ScrollbarWrapper'].style[vendor + 'TransitionDelay'] = '0'; | |
that[dir + 'ScrollbarWrapper'].style.opacity = hidden && that.options.hideScrollbar ? '0' : '1'; | |
that[dir + 'ScrollbarIndicator'].style[vendor + 'Transform'] = trnOpen + (dir == 'h' ? pos + 'px,0' : '0,' + pos + 'px') + trnClose; | |
}, | |
_start: function (e) { | |
var that = this, | |
point = hasTouch ? e.touches[0] : e, | |
matrix, x, y, | |
c1, c2; | |
if (!that.enabled) return; | |
if (that.options.onBeforeScrollStart) that.options.onBeforeScrollStart.call(that, e); | |
if (that.options.useTransition || that.options.zoom) that._transitionTime(0); | |
that.moved = false; | |
that.animating = false; | |
that.zoomed = false; | |
that.distX = 0; | |
that.distY = 0; | |
that.absDistX = 0; | |
that.absDistY = 0; | |
that.dirX = 0; | |
that.dirY = 0; | |
// Gesture start | |
if (that.options.zoom && hasTouch && e.touches.length > 1) { | |
c1 = m.abs(e.touches[0].pageX-e.touches[1].pageX); | |
c2 = m.abs(e.touches[0].pageY-e.touches[1].pageY); | |
that.touchesDistStart = m.sqrt(c1 * c1 + c2 * c2); | |
that.originX = m.abs(e.touches[0].pageX + e.touches[1].pageX - that.wrapperOffsetLeft * 2) / 2 - that.x; | |
that.originY = m.abs(e.touches[0].pageY + e.touches[1].pageY - that.wrapperOffsetTop * 2) / 2 - that.y; | |
if (that.options.onZoomStart) that.options.onZoomStart.call(that, e); | |
} | |
if (that.options.momentum) { | |
if (that.options.useTransform) { | |
// Very lame general purpose alternative to CSSMatrix | |
matrix = getComputedStyle(that.scroller, null)[vendor + 'Transform'].replace(/[^0-9-.,]/g, '').split(','); | |
x = matrix[4] * 1; | |
y = matrix[5] * 1; | |
} else { | |
x = getComputedStyle(that.scroller, null).left.replace(/[^0-9-]/g, '') * 1; | |
y = getComputedStyle(that.scroller, null).top.replace(/[^0-9-]/g, '') * 1; | |
} | |
if (x != that.x || y != that.y) { | |
if (that.options.useTransition) that._unbind('webkitTransitionEnd'); | |
else cancelFrame(that.aniTime); | |
that.steps = []; | |
that._pos(x, y); | |
} | |
} | |
that.absStartX = that.x; // Needed by snap threshold | |
that.absStartY = that.y; | |
that.startX = that.x; | |
that.startY = that.y; | |
that.pointX = point.pageX; | |
that.pointY = point.pageY; | |
that.startTime = e.timeStamp || Date.now(); | |
if (that.options.onScrollStart) that.options.onScrollStart.call(that, e); | |
that._bind(MOVE_EV); | |
that._bind(END_EV); | |
that._bind(CANCEL_EV); | |
}, | |
_move: function (e) { | |
var that = this, | |
point = hasTouch ? e.touches[0] : e, | |
deltaX = point.pageX - that.pointX, | |
deltaY = point.pageY - that.pointY, | |
newX = that.x + deltaX, | |
newY = that.y + deltaY, | |
c1, c2, scale, | |
timestamp = e.timeStamp || Date.now(); | |
if (that.options.onBeforeScrollMove) that.options.onBeforeScrollMove.call(that, e); | |
// Zoom | |
if (that.options.zoom && hasTouch && e.touches.length > 1) { | |
c1 = m.abs(e.touches[0].pageX - e.touches[1].pageX); | |
c2 = m.abs(e.touches[0].pageY - e.touches[1].pageY); | |
that.touchesDist = m.sqrt(c1*c1+c2*c2); | |
that.zoomed = true; | |
scale = 1 / that.touchesDistStart * that.touchesDist * this.scale; | |
if (scale < that.options.zoomMin) scale = 0.5 * that.options.zoomMin * Math.pow(2.0, scale / that.options.zoomMin); | |
else if (scale > that.options.zoomMax) scale = 2.0 * that.options.zoomMax * Math.pow(0.5, that.options.zoomMax / scale); | |
that.lastScale = scale / this.scale; | |
newX = this.originX - this.originX * that.lastScale + this.x, | |
newY = this.originY - this.originY * that.lastScale + this.y; | |
this.scroller.style[vendor + 'Transform'] = trnOpen + newX + 'px,' + newY + 'px' + trnClose + ' scale(' + scale + ')'; | |
if (that.options.onZoom) that.options.onZoom.call(that, e); | |
return; | |
} | |
that.pointX = point.pageX; | |
that.pointY = point.pageY; | |
// Slow down if outside of the boundaries | |
if (newX > 0 || newX < that.maxScrollX) { | |
newX = that.options.bounce ? that.x + (deltaX / 2) : newX >= 0 || that.maxScrollX >= 0 ? 0 : that.maxScrollX; | |
} | |
if (newY > that.minScrollY || newY < that.maxScrollY) { | |
newY = that.options.bounce ? that.y + (deltaY / 2) : newY >= that.minScrollY || that.maxScrollY >= 0 ? that.minScrollY : that.maxScrollY; | |
} | |
if (that.absDistX < 6 && that.absDistY < 6) { | |
that.distX += deltaX; | |
that.distY += deltaY; | |
that.absDistX = m.abs(that.distX); | |
that.absDistY = m.abs(that.distY); | |
return; | |
} | |
// Lock direction | |
if (that.options.lockDirection) { | |
if (that.absDistX > that.absDistY + 5) { | |
newY = that.y; | |
deltaY = 0; | |
} else if (that.absDistY > that.absDistX + 5) { | |
newX = that.x; | |
deltaX = 0; | |
} | |
} | |
that.moved = true; | |
that._pos(newX, newY); | |
that.dirX = deltaX > 0 ? -1 : deltaX < 0 ? 1 : 0; | |
that.dirY = deltaY > 0 ? -1 : deltaY < 0 ? 1 : 0; | |
if (timestamp - that.startTime > 300) { | |
that.startTime = timestamp; | |
that.startX = that.x; | |
that.startY = that.y; | |
} | |
if (that.options.onScrollMove) that.options.onScrollMove.call(that, e); | |
}, | |
_end: function (e) { | |
if (hasTouch && e.touches.length != 0) return; | |
var that = this, | |
point = hasTouch ? e.changedTouches[0] : e, | |
target, ev, | |
momentumX = { dist:0, time:0 }, | |
momentumY = { dist:0, time:0 }, | |
duration = (e.timeStamp || Date.now()) - that.startTime, | |
newPosX = that.x, | |
newPosY = that.y, | |
distX, distY, | |
newDuration, | |
snap, | |
scale; | |
that._unbind(MOVE_EV); | |
that._unbind(END_EV); | |
that._unbind(CANCEL_EV); | |
if (that.options.onBeforeScrollEnd) that.options.onBeforeScrollEnd.call(that, e); | |
if (that.zoomed) { | |
scale = that.scale * that.lastScale; | |
scale = Math.max(that.options.zoomMin, scale); | |
scale = Math.min(that.options.zoomMax, scale); | |
that.lastScale = scale / that.scale; | |
that.scale = scale; | |
that.x = that.originX - that.originX * that.lastScale + that.x; | |
that.y = that.originY - that.originY * that.lastScale + that.y; | |
that.scroller.style[vendor + 'TransitionDuration'] = '200ms'; | |
that.scroller.style[vendor + 'Transform'] = trnOpen + that.x + 'px,' + that.y + 'px' + trnClose + ' scale(' + that.scale + ')'; | |
that.zoomed = false; | |
that.refresh(); | |
if (that.options.onZoomEnd) that.options.onZoomEnd.call(that, e); | |
return; | |
} | |
if (!that.moved) { | |
if (hasTouch) { | |
if (that.doubleTapTimer && that.options.zoom) { | |
// Double tapped | |
clearTimeout(that.doubleTapTimer); | |
that.doubleTapTimer = null; | |
if (that.options.onZoomStart) that.options.onZoomStart.call(that, e); | |
that.zoom(that.pointX, that.pointY, that.scale == 1 ? that.options.doubleTapZoom : 1); | |
if (that.options.onZoomEnd) { | |
setTimeout(function() { | |
that.options.onZoomEnd.call(that, e); | |
}, 200); // 200 is default zoom duration | |
} | |
} else { | |
that.doubleTapTimer = setTimeout(function () { | |
that.doubleTapTimer = null; | |
// Find the last touched element | |
target = point.target; | |
while (target.nodeType != 1) target = target.parentNode; | |
if (target.tagName != 'SELECT' && target.tagName != 'INPUT' && target.tagName != 'TEXTAREA') { | |
ev = document.createEvent('MouseEvents'); | |
ev.initMouseEvent('click', true, true, e.view, 1, | |
point.screenX, point.screenY, point.clientX, point.clientY, | |
e.ctrlKey, e.altKey, e.shiftKey, e.metaKey, | |
0, null); | |
ev._fake = true; | |
target.dispatchEvent(ev); | |
} | |
}, that.options.zoom ? 250 : 0); | |
} | |
} | |
that._resetPos(200); | |
if (that.options.onTouchEnd) that.options.onTouchEnd.call(that, e); | |
return; | |
} | |
if (duration < 300 && that.options.momentum) { | |
momentumX = newPosX ? that._momentum(newPosX - that.startX, duration, -that.x, that.scrollerW - that.wrapperW + that.x, that.options.bounce ? that.wrapperW : 0) : momentumX; | |
momentumY = newPosY ? that._momentum(newPosY - that.startY, duration, -that.y, (that.maxScrollY < 0 ? that.scrollerH - that.wrapperH + that.y - that.minScrollY : 0), that.options.bounce ? that.wrapperH : 0) : momentumY; | |
newPosX = that.x + momentumX.dist; | |
newPosY = that.y + momentumY.dist; | |
if ((that.x > 0 && newPosX > 0) || (that.x < that.maxScrollX && newPosX < that.maxScrollX)) momentumX = { dist:0, time:0 }; | |
if ((that.y > that.minScrollY && newPosY > that.minScrollY) || (that.y < that.maxScrollY && newPosY < that.maxScrollY)) momentumY = { dist:0, time:0 }; | |
} | |
if (momentumX.dist || momentumY.dist) { | |
newDuration = m.max(m.max(momentumX.time, momentumY.time), 10); | |
// Do we need to snap? | |
if (that.options.snap) { | |
distX = newPosX - that.absStartX; | |
distY = newPosY - that.absStartY; | |
if (m.abs(distX) < that.options.snapThreshold && m.abs(distY) < that.options.snapThreshold) { that.scrollTo(that.absStartX, that.absStartY, 200); } | |
else { | |
snap = that._snap(newPosX, newPosY); | |
newPosX = snap.x; | |
newPosY = snap.y; | |
newDuration = m.max(snap.time, newDuration); | |
} | |
} | |
that.scrollTo(mround(newPosX), mround(newPosY), newDuration); | |
if (that.options.onTouchEnd) that.options.onTouchEnd.call(that, e); | |
return; | |
} | |
// Do we need to snap? | |
if (that.options.snap) { | |
distX = newPosX - that.absStartX; | |
distY = newPosY - that.absStartY; | |
if (m.abs(distX) < that.options.snapThreshold && m.abs(distY) < that.options.snapThreshold) that.scrollTo(that.absStartX, that.absStartY, 200); | |
else { | |
snap = that._snap(that.x, that.y); | |
if (snap.x != that.x || snap.y != that.y) that.scrollTo(snap.x, snap.y, snap.time); | |
} | |
if (that.options.onTouchEnd) that.options.onTouchEnd.call(that, e); | |
return; | |
} | |
that._resetPos(200); | |
if (that.options.onTouchEnd) that.options.onTouchEnd.call(that, e); | |
}, | |
_resetPos: function (time) { | |
var that = this, | |
resetX = that.x >= 0 ? 0 : that.x < that.maxScrollX ? that.maxScrollX : that.x, | |
resetY = that.y >= that.minScrollY || that.maxScrollY > 0 ? that.minScrollY : that.y < that.maxScrollY ? that.maxScrollY : that.y; | |
if (resetX == that.x && resetY == that.y) { | |
if (that.moved) { | |
that.moved = false; | |
if (that.options.onScrollEnd) that.options.onScrollEnd.call(that); // Execute custom code on scroll end | |
} | |
if (that.hScrollbar && that.options.hideScrollbar) { | |
if (vendor == 'webkit') that.hScrollbarWrapper.style[vendor + 'TransitionDelay'] = '300ms'; | |
that.hScrollbarWrapper.style.opacity = '0'; | |
} | |
if (that.vScrollbar && that.options.hideScrollbar) { | |
if (vendor == 'webkit') that.vScrollbarWrapper.style[vendor + 'TransitionDelay'] = '300ms'; | |
that.vScrollbarWrapper.style.opacity = '0'; | |
} | |
return; | |
} | |
that.scrollTo(resetX, resetY, time || 0); | |
}, | |
_wheel: function (e) { | |
var that = this, | |
wheelDeltaX, wheelDeltaY, | |
deltaX, deltaY, | |
deltaScale; | |
if ('wheelDeltaX' in e) { | |
wheelDeltaX = e.wheelDeltaX / 12; | |
wheelDeltaY = e.wheelDeltaY / 12; | |
} else if ('detail' in e) { | |
wheelDeltaX = wheelDeltaY = -e.detail * 3; | |
} else { | |
wheelDeltaX = wheelDeltaY = -e.wheelDelta; | |
} | |
if (that.options.wheelAction == 'zoom') { | |
deltaScale = that.scale * Math.pow(2, 1/3 * (wheelDeltaY ? wheelDeltaY / Math.abs(wheelDeltaY) : 0)); | |
if (deltaScale < that.options.zoomMin) deltaScale = that.options.zoomMin; | |
if (deltaScale > that.options.zoomMax) deltaScale = that.options.zoomMax; | |
if (deltaScale != that.scale) { | |
if (!that.wheelZoomCount && that.options.onZoomStart) that.options.onZoomStart.call(that, e); | |
that.wheelZoomCount++; | |
that.zoom(e.pageX, e.pageY, deltaScale, 400); | |
setTimeout(function() { | |
that.wheelZoomCount--; | |
if (!that.wheelZoomCount && that.options.onZoomEnd) that.options.onZoomEnd.call(that, e); | |
}, 400); | |
} | |
return; | |
} | |
deltaX = that.x + wheelDeltaX; | |
deltaY = that.y + wheelDeltaY; | |
if (deltaX > 0) deltaX = 0; | |
else if (deltaX < that.maxScrollX) deltaX = that.maxScrollX; | |
if (deltaY > that.minScrollY) deltaY = that.minScrollY; | |
else if (deltaY < that.maxScrollY) deltaY = that.maxScrollY; | |
that.scrollTo(deltaX, deltaY, 0); | |
}, | |
_mouseout: function (e) { | |
var t = e.relatedTarget; | |
if (!t) { | |
this._end(e); | |
return; | |
} | |
while (t = t.parentNode) if (t == this.wrapper) return; | |
this._end(e); | |
}, | |
_transitionEnd: function (e) { | |
var that = this; | |
if (e.target != that.scroller) return; | |
that._unbind('webkitTransitionEnd'); | |
that._startAni(); | |
}, | |
/** | |
* | |
* Utilities | |
* | |
*/ | |
_startAni: function () { | |
var that = this, | |
startX = that.x, startY = that.y, | |
startTime = Date.now(), | |
step, easeOut, | |
animate; | |
if (that.animating) return; | |
if (!that.steps.length) { | |
that._resetPos(400); | |
return; | |
} | |
step = that.steps.shift(); | |
if (step.x == startX && step.y == startY) step.time = 0; | |
that.animating = true; | |
that.moved = true; | |
if (that.options.useTransition) { | |
that._transitionTime(step.time); | |
that._pos(step.x, step.y); | |
that.animating = false; | |
if (step.time) that._bind('webkitTransitionEnd'); | |
else that._resetPos(0); | |
return; | |
} | |
animate = function () { | |
var now = Date.now(), | |
newX, newY; | |
if (now >= startTime + step.time) { | |
that._pos(step.x, step.y); | |
that.animating = false; | |
if (that.options.onAnimationEnd) that.options.onAnimationEnd.call(that); // Execute custom code on animation end | |
that._startAni(); | |
return; | |
} | |
now = (now - startTime) / step.time - 1; | |
easeOut = m.sqrt(1 - now * now); | |
newX = (step.x - startX) * easeOut + startX; | |
newY = (step.y - startY) * easeOut + startY; | |
that._pos(newX, newY); | |
if (that.animating) that.aniTime = nextFrame(animate); | |
}; | |
animate(); | |
}, | |
_transitionTime: function (time) { | |
time += 'ms'; | |
this.scroller.style[vendor + 'TransitionDuration'] = time; | |
if (this.hScrollbar) this.hScrollbarIndicator.style[vendor + 'TransitionDuration'] = time; | |
if (this.vScrollbar) this.vScrollbarIndicator.style[vendor + 'TransitionDuration'] = time; | |
}, | |
_momentum: function (dist, time, maxDistUpper, maxDistLower, size) { | |
var deceleration = 0.0006, | |
speed = m.abs(dist) / time, | |
newDist = (speed * speed) / (2 * deceleration), | |
newTime = 0, outsideDist = 0; | |
// Proportinally reduce speed if we are outside of the boundaries | |
if (dist > 0 && newDist > maxDistUpper) { | |
outsideDist = size / (6 / (newDist / speed * deceleration)); | |
maxDistUpper = maxDistUpper + outsideDist; | |
speed = speed * maxDistUpper / newDist; | |
newDist = maxDistUpper; | |
} else if (dist < 0 && newDist > maxDistLower) { | |
outsideDist = size / (6 / (newDist / speed * deceleration)); | |
maxDistLower = maxDistLower + outsideDist; | |
speed = speed * maxDistLower / newDist; | |
newDist = maxDistLower; | |
} | |
newDist = newDist * (dist < 0 ? -1 : 1); | |
newTime = speed / deceleration; | |
return { dist: newDist, time: mround(newTime) }; | |
}, | |
_offset: function (el) { | |
var left = -el.offsetLeft, | |
top = -el.offsetTop; | |
while (el = el.offsetParent) { | |
left -= el.offsetLeft; | |
top -= el.offsetTop; | |
} | |
if (el != this.wrapper) { | |
left *= this.scale; | |
top *= this.scale; | |
} | |
return { left: left, top: top }; | |
}, | |
_snap: function (x, y) { | |
var that = this, | |
i, l, | |
page, time, | |
sizeX, sizeY; | |
// Check page X | |
page = that.pagesX.length - 1; | |
for (i=0, l=that.pagesX.length; i<l; i++) { | |
if (x >= that.pagesX[i]) { | |
page = i; | |
break; | |
} | |
} | |
if (page == that.currPageX && page > 0 && that.dirX < 0) page--; | |
x = that.pagesX[page]; | |
sizeX = m.abs(x - that.pagesX[that.currPageX]); | |
sizeX = sizeX ? m.abs(that.x - x) / sizeX * 500 : 0; | |
that.currPageX = page; | |
// Check page Y | |
page = that.pagesY.length-1; | |
for (i=0; i<page; i++) { | |
if (y >= that.pagesY[i]) { | |
page = i; | |
break; | |
} | |
} | |
if (page == that.currPageY && page > 0 && that.dirY < 0) page--; | |
y = that.pagesY[page]; | |
sizeY = m.abs(y - that.pagesY[that.currPageY]); | |
sizeY = sizeY ? m.abs(that.y - y) / sizeY * 500 : 0; | |
that.currPageY = page; | |
// Snap with constant speed (proportional duration) | |
time = mround(m.max(sizeX, sizeY)) || 200; | |
return { x: x, y: y, time: time }; | |
}, | |
_bind: function (type, el, bubble) { | |
(el || this.scroller).addEventListener(type, this, !!bubble); | |
}, | |
_unbind: function (type, el, bubble) { | |
(el || this.scroller).removeEventListener(type, this, !!bubble); | |
}, | |
/** | |
* | |
* Public methods | |
* | |
*/ | |
destroy: function () { | |
var that = this; | |
that.scroller.style[vendor + 'Transform'] = ''; | |
// Remove the scrollbars | |
that.hScrollbar = false; | |
that.vScrollbar = false; | |
that._scrollbar('h'); | |
that._scrollbar('v'); | |
// Remove the event listeners | |
that._unbind(RESIZE_EV, window); | |
that._unbind(START_EV); | |
that._unbind(MOVE_EV); | |
that._unbind(END_EV); | |
that._unbind(CANCEL_EV); | |
if (that.options.hasTouch) { | |
that._unbind('mouseout', that.wrapper); | |
that._unbind(WHEEL_EV); | |
} | |
if (that.options.useTransition) that._unbind('webkitTransitionEnd'); | |
if (that.options.checkDOMChanges) clearInterval(that.checkDOMTime); | |
if (that.options.onDestroy) that.options.onDestroy.call(that); | |
}, | |
refresh: function () { | |
var that = this, | |
offset, | |
i, l, | |
els, | |
pos = 0, | |
page = 0; | |
if (that.scale < that.options.zoomMin) that.scale = that.options.zoomMin; | |
that.wrapperW = that.wrapper.clientWidth || 1; | |
that.wrapperH = that.wrapper.clientHeight || 1; | |
that.minScrollY = -that.options.topOffset || 0; | |
that.scrollerW = mround(that.scroller.offsetWidth * that.scale); | |
that.scrollerH = mround((that.scroller.offsetHeight + that.minScrollY) * that.scale); | |
that.maxScrollX = that.wrapperW - that.scrollerW; | |
that.maxScrollY = that.wrapperH - that.scrollerH + that.minScrollY; | |
that.dirX = 0; | |
that.dirY = 0; | |
if (that.options.onRefresh) that.options.onRefresh.call(that); | |
that.hScroll = that.options.hScroll && that.maxScrollX < 0; | |
that.vScroll = that.options.vScroll && (!that.options.bounceLock && !that.hScroll || that.scrollerH > that.wrapperH); | |
that.hScrollbar = that.hScroll && that.options.hScrollbar; | |
that.vScrollbar = that.vScroll && that.options.vScrollbar && that.scrollerH > that.wrapperH; | |
offset = that._offset(that.wrapper); | |
that.wrapperOffsetLeft = -offset.left; | |
that.wrapperOffsetTop = -offset.top; | |
// Prepare snap | |
if (typeof that.options.snap == 'string') { | |
that.pagesX = []; | |
that.pagesY = []; | |
els = that.scroller.querySelectorAll(that.options.snap); | |
for (i=0, l=els.length; i<l; i++) { | |
pos = that._offset(els[i]); | |
pos.left += that.wrapperOffsetLeft; | |
pos.top += that.wrapperOffsetTop; | |
that.pagesX[i] = pos.left < that.maxScrollX ? that.maxScrollX : pos.left * that.scale; | |
that.pagesY[i] = pos.top < that.maxScrollY ? that.maxScrollY : pos.top * that.scale; | |
} | |
} else if (that.options.snap) { | |
that.pagesX = []; | |
while (pos >= that.maxScrollX) { | |
that.pagesX[page] = pos; | |
pos = pos - that.wrapperW; | |
page++; | |
} | |
if (that.maxScrollX%that.wrapperW) that.pagesX[that.pagesX.length] = that.maxScrollX - that.pagesX[that.pagesX.length-1] + that.pagesX[that.pagesX.length-1]; | |
pos = 0; | |
page = 0; | |
that.pagesY = []; | |
while (pos >= that.maxScrollY) { | |
that.pagesY[page] = pos; | |
pos = pos - that.wrapperH; | |
page++; | |
} | |
if (that.maxScrollY%that.wrapperH) that.pagesY[that.pagesY.length] = that.maxScrollY - that.pagesY[that.pagesY.length-1] + that.pagesY[that.pagesY.length-1]; | |
} | |
// Prepare the scrollbars | |
that._scrollbar('h'); | |
that._scrollbar('v'); | |
if (!that.zoomed) { | |
that.scroller.style[vendor + 'TransitionDuration'] = '0'; | |
that._resetPos(200); | |
} | |
}, | |
scrollTo: function (x, y, time, relative) { | |
var that = this, | |
step = x, | |
i, l; | |
that.stop(); | |
if (!step.length) step = [{ x: x, y: y, time: time, relative: relative }]; | |
for (i=0, l=step.length; i<l; i++) { | |
if (step[i].relative) { step[i].x = that.x - step[i].x; step[i].y = that.y - step[i].y; } | |
that.steps.push({ x: step[i].x, y: step[i].y, time: step[i].time || 0 }); | |
} | |
that._startAni(); | |
}, | |
scrollToElement: function (el, time) { | |
var that = this, pos; | |
el = el.nodeType ? el : that.scroller.querySelector(el); | |
if (!el) return; | |
pos = that._offset(el); | |
pos.left += that.wrapperOffsetLeft; | |
pos.top += that.wrapperOffsetTop; | |
pos.left = pos.left > 0 ? 0 : pos.left < that.maxScrollX ? that.maxScrollX : pos.left; | |
pos.top = pos.top > that.minScrollY ? that.minScrollY : pos.top < that.maxScrollY ? that.maxScrollY : pos.top; | |
time = time === undefined ? m.max(m.abs(pos.left)*2, m.abs(pos.top)*2) : time; | |
that.scrollTo(pos.left, pos.top, time); | |
}, | |
scrollToPage: function (pageX, pageY, time) { | |
var that = this, x, y; | |
if (that.options.onScrollStart) that.options.onScrollStart.call(that); | |
if (that.options.snap) { | |
pageX = pageX == 'next' ? that.currPageX+1 : pageX == 'prev' ? that.currPageX-1 : pageX; | |
pageY = pageY == 'next' ? that.currPageY+1 : pageY == 'prev' ? that.currPageY-1 : pageY; | |
pageX = pageX < 0 ? 0 : pageX > that.pagesX.length-1 ? that.pagesX.length-1 : pageX; | |
pageY = pageY < 0 ? 0 : pageY > that.pagesY.length-1 ? that.pagesY.length-1 : pageY; | |
that.currPageX = pageX; | |
that.currPageY = pageY; | |
x = that.pagesX[pageX]; | |
y = that.pagesY[pageY]; | |
} else { | |
x = -that.wrapperW * pageX; | |
y = -that.wrapperH * pageY; | |
if (x < that.maxScrollX) x = that.maxScrollX; | |
if (y < that.maxScrollY) y = that.maxScrollY; | |
} | |
that.scrollTo(x, y, time || 400); | |
}, | |
disable: function () { | |
this.stop(); | |
this._resetPos(0); | |
this.enabled = false; | |
// If disabled after touchstart we make sure that there are no left over events | |
this._unbind(MOVE_EV); | |
this._unbind(END_EV); | |
this._unbind(CANCEL_EV); | |
}, | |
enable: function () { | |
this.enabled = true; | |
}, | |
stop: function () { | |
if (this.options.useTransition) this._unbind('webkitTransitionEnd'); | |
else cancelFrame(this.aniTime); | |
this.steps = []; | |
this.moved = false; | |
this.animating = false; | |
}, | |
zoom: function (x, y, scale, time) { | |
var that = this, | |
relScale = scale / that.scale; | |
if (!that.options.useTransform) return; | |
that.zoomed = true; | |
time = time === undefined ? 200 : time; | |
x = x - that.wrapperOffsetLeft - that.x; | |
y = y - that.wrapperOffsetTop - that.y; | |
that.x = x - x * relScale + that.x; | |
that.y = y - y * relScale + that.y; | |
that.scale = scale; | |
that.refresh(); | |
that.x = that.x > 0 ? 0 : that.x < that.maxScrollX ? that.maxScrollX : that.x; | |
that.y = that.y > that.minScrollY ? that.minScrollY : that.y < that.maxScrollY ? that.maxScrollY : that.y; | |
that.scroller.style[vendor + 'TransitionDuration'] = time + 'ms'; | |
that.scroller.style[vendor + 'Transform'] = trnOpen + that.x + 'px,' + that.y + 'px' + trnClose + ' scale(' + scale + ')'; | |
that.zoomed = false; | |
}, | |
isReady: function () { | |
return !this.moved && !this.zoomed && !this.animating; | |
} | |
}; | |
if (typeof exports !== 'undefined') exports.iScroll = iScroll; | |
else window.iScroll = iScroll; | |
})(); | |
window.ninemsn.portal.common.video.init(); | |
})(ninemsn.portal.common.video.$, ninemsn); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment