Adding mapbox-gl branch
This commit is contained in:
@@ -0,0 +1,631 @@
|
||||
// Copyright 2009 The Closure Library Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS-IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
/**
|
||||
* @fileoverview Wrapper on a Flash object embedded in the HTML page.
|
||||
* This class contains routines for writing the HTML to create the Flash object
|
||||
* using a goog.ui.Component approach. Tested on Firefox 1.5, 2 and 3, IE6, 7,
|
||||
* Konqueror, Chrome and Safari.
|
||||
*
|
||||
* Based on http://go/flashobject.js
|
||||
*
|
||||
* Based on the following compatibility test suite:
|
||||
* http://www.bobbyvandersluis.com/flashembed/testsuite/
|
||||
*
|
||||
* TODO(user): take a look at swfobject, and maybe use it instead of the current
|
||||
* flash embedding method.
|
||||
*
|
||||
* Examples of usage:
|
||||
*
|
||||
* <pre>
|
||||
* var url = goog.html.TrustedResourceUrl.fromConstant(
|
||||
* goog.string.Const.from('https://hostname/flash.swf'))
|
||||
* var flash = new goog.ui.media.FlashObject(url);
|
||||
* flash.setFlashVar('myvar', 'foo');
|
||||
* flash.render(goog.dom.getElement('parent'));
|
||||
* </pre>
|
||||
*
|
||||
* TODO(user, jessan): create a goog.ui.media.BrowserInterfaceFlashObject that
|
||||
* subclasses goog.ui.media.FlashObject to provide all the goodness of
|
||||
* http://go/browserinterface.as
|
||||
*
|
||||
*/
|
||||
|
||||
goog.provide('goog.ui.media.FlashObject');
|
||||
goog.provide('goog.ui.media.FlashObject.ScriptAccessLevel');
|
||||
goog.provide('goog.ui.media.FlashObject.Wmodes');
|
||||
|
||||
goog.require('goog.asserts');
|
||||
goog.require('goog.dom.safe');
|
||||
goog.require('goog.events.Event');
|
||||
goog.require('goog.events.EventHandler');
|
||||
goog.require('goog.events.EventType');
|
||||
goog.require('goog.html.SafeUrl');
|
||||
goog.require('goog.html.TrustedResourceUrl');
|
||||
goog.require('goog.html.flash');
|
||||
goog.require('goog.html.legacyconversions');
|
||||
goog.require('goog.log');
|
||||
goog.require('goog.object');
|
||||
goog.require('goog.string');
|
||||
goog.require('goog.structs.Map');
|
||||
goog.require('goog.style');
|
||||
goog.require('goog.ui.Component');
|
||||
goog.require('goog.userAgent');
|
||||
goog.require('goog.userAgent.flash');
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* A very simple flash wrapper, that allows you to create flash object
|
||||
* programmatically, instead of embedding your own HTML. It extends
|
||||
* {@link goog.ui.Component}, which makes it very easy to be embedded on the
|
||||
* page.
|
||||
*
|
||||
* @param {string|!goog.html.TrustedResourceUrl} flashUrl The Flash SWF URL.
|
||||
* If possible pass a TrustedResourceUrl. string is supported
|
||||
* for backwards-compatibility only, uses goog.html.legacyconversions,
|
||||
* and will be sanitized with goog.html.SafeUrl.sanitize() before being
|
||||
* used.
|
||||
* @param {goog.dom.DomHelper=} opt_domHelper An optional DomHelper.
|
||||
* @extends {goog.ui.Component}
|
||||
* @constructor
|
||||
*/
|
||||
goog.ui.media.FlashObject = function(flashUrl, opt_domHelper) {
|
||||
goog.ui.Component.call(this, opt_domHelper);
|
||||
|
||||
var trustedResourceUrl;
|
||||
if (flashUrl instanceof goog.html.TrustedResourceUrl) {
|
||||
trustedResourceUrl = flashUrl;
|
||||
} else {
|
||||
var flashUrlSanitized =
|
||||
goog.html.SafeUrl.unwrap(goog.html.SafeUrl.sanitize(flashUrl));
|
||||
trustedResourceUrl = goog.html.legacyconversions
|
||||
.trustedResourceUrlFromString(flashUrlSanitized);
|
||||
}
|
||||
|
||||
/**
|
||||
* The URL of the flash movie to be embedded.
|
||||
*
|
||||
* @type {!goog.html.TrustedResourceUrl}
|
||||
* @private
|
||||
*/
|
||||
this.flashUrl_ = trustedResourceUrl;
|
||||
|
||||
/**
|
||||
* An event handler used to handle events consistently between browsers.
|
||||
* @type {goog.events.EventHandler<!goog.ui.media.FlashObject>}
|
||||
* @private
|
||||
*/
|
||||
this.eventHandler_ = new goog.events.EventHandler(this);
|
||||
|
||||
/**
|
||||
* A map of variables to be passed to the flash movie.
|
||||
*
|
||||
* @type {goog.structs.Map}
|
||||
* @private
|
||||
*/
|
||||
this.flashVars_ = new goog.structs.Map();
|
||||
};
|
||||
goog.inherits(goog.ui.media.FlashObject, goog.ui.Component);
|
||||
|
||||
|
||||
/**
|
||||
* Different states of loaded-ness in which the SWF itself can be
|
||||
*
|
||||
* Talked about at:
|
||||
* http://kb.adobe.com/selfservice/viewContent.do?externalId=tn_12059&sliceId=1
|
||||
*
|
||||
* @enum {number}
|
||||
* @private
|
||||
*/
|
||||
goog.ui.media.FlashObject.SwfReadyStates_ = {
|
||||
LOADING: 0,
|
||||
UNINITIALIZED: 1,
|
||||
LOADED: 2,
|
||||
INTERACTIVE: 3,
|
||||
COMPLETE: 4
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* The different modes for displaying a SWF. Note that different wmodes
|
||||
* can result in different bugs in different browsers and also that
|
||||
* both OPAQUE and TRANSPARENT will result in a performance hit.
|
||||
*
|
||||
* @enum {string}
|
||||
*/
|
||||
goog.ui.media.FlashObject.Wmodes = {
|
||||
/**
|
||||
* Allows for z-ordering of the SWF.
|
||||
*/
|
||||
OPAQUE: 'opaque',
|
||||
|
||||
/**
|
||||
* Allows for z-ordering of the SWF and plays the SWF with a transparent BG.
|
||||
*/
|
||||
TRANSPARENT: 'transparent',
|
||||
|
||||
/**
|
||||
* The default wmode. Does not allow for z-ordering of the SWF.
|
||||
*/
|
||||
WINDOW: 'window'
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* The different levels of allowScriptAccess.
|
||||
*
|
||||
* Talked about at:
|
||||
* http://kb2.adobe.com/cps/164/tn_16494.html
|
||||
*
|
||||
* @enum {string}
|
||||
*/
|
||||
goog.ui.media.FlashObject.ScriptAccessLevel = {
|
||||
/*
|
||||
* The flash object can always communicate with its container page.
|
||||
*/
|
||||
ALWAYS: 'always',
|
||||
|
||||
/*
|
||||
* The flash object can only communicate with its container page if they are
|
||||
* hosted in the same domain.
|
||||
*/
|
||||
SAME_DOMAIN: 'sameDomain',
|
||||
|
||||
/*
|
||||
* The flash can not communicate with its container page.
|
||||
*/
|
||||
NEVER: 'never'
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* The component CSS namespace.
|
||||
*
|
||||
* @type {string}
|
||||
*/
|
||||
goog.ui.media.FlashObject.CSS_CLASS = goog.getCssName('goog-ui-media-flash');
|
||||
|
||||
|
||||
/**
|
||||
* The flash object CSS class.
|
||||
*
|
||||
* @type {string}
|
||||
*/
|
||||
goog.ui.media.FlashObject.FLASH_CSS_CLASS =
|
||||
goog.getCssName('goog-ui-media-flash-object');
|
||||
|
||||
|
||||
/**
|
||||
* A logger used for debugging.
|
||||
*
|
||||
* @type {goog.log.Logger}
|
||||
* @private
|
||||
*/
|
||||
goog.ui.media.FlashObject.prototype.logger_ =
|
||||
goog.log.getLogger('goog.ui.media.FlashObject');
|
||||
|
||||
|
||||
/**
|
||||
* The wmode for the SWF.
|
||||
*
|
||||
* @type {goog.ui.media.FlashObject.Wmodes}
|
||||
* @private
|
||||
*/
|
||||
goog.ui.media.FlashObject.prototype.wmode_ =
|
||||
goog.ui.media.FlashObject.Wmodes.WINDOW;
|
||||
|
||||
|
||||
/**
|
||||
* The minimum required flash version.
|
||||
*
|
||||
* @type {?string}
|
||||
* @private
|
||||
*/
|
||||
goog.ui.media.FlashObject.prototype.requiredVersion_;
|
||||
|
||||
|
||||
/**
|
||||
* The flash movie width.
|
||||
*
|
||||
* @type {string}
|
||||
* @private
|
||||
*/
|
||||
goog.ui.media.FlashObject.prototype.width_;
|
||||
|
||||
|
||||
/**
|
||||
* The flash movie height.
|
||||
*
|
||||
* @type {string}
|
||||
* @private
|
||||
*/
|
||||
goog.ui.media.FlashObject.prototype.height_;
|
||||
|
||||
|
||||
/**
|
||||
* The flash movie background color.
|
||||
*
|
||||
* @type {string}
|
||||
* @private
|
||||
*/
|
||||
goog.ui.media.FlashObject.prototype.backgroundColor_ = '#000000';
|
||||
|
||||
|
||||
/**
|
||||
* The flash movie allowScriptAccess setting.
|
||||
*
|
||||
* @type {string}
|
||||
* @private
|
||||
*/
|
||||
goog.ui.media.FlashObject.prototype.allowScriptAccess_ =
|
||||
goog.ui.media.FlashObject.ScriptAccessLevel.SAME_DOMAIN;
|
||||
|
||||
|
||||
/**
|
||||
* Sets the flash movie Wmode.
|
||||
*
|
||||
* @param {goog.ui.media.FlashObject.Wmodes} wmode the flash movie Wmode.
|
||||
* @return {!goog.ui.media.FlashObject} The flash object instance for chaining.
|
||||
*/
|
||||
goog.ui.media.FlashObject.prototype.setWmode = function(wmode) {
|
||||
this.wmode_ = wmode;
|
||||
return this;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @return {string} Returns the flash movie wmode.
|
||||
*/
|
||||
goog.ui.media.FlashObject.prototype.getWmode = function() {
|
||||
return this.wmode_;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Adds flash variables.
|
||||
*
|
||||
* @param {goog.structs.Map|Object} map A key-value map of variables.
|
||||
* @return {!goog.ui.media.FlashObject} The flash object instance for chaining.
|
||||
*/
|
||||
goog.ui.media.FlashObject.prototype.addFlashVars = function(map) {
|
||||
this.flashVars_.addAll(map);
|
||||
return this;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Sets a flash variable.
|
||||
*
|
||||
* @param {string} key The name of the flash variable.
|
||||
* @param {string} value The value of the flash variable.
|
||||
* @return {!goog.ui.media.FlashObject} The flash object instance for chaining.
|
||||
*/
|
||||
goog.ui.media.FlashObject.prototype.setFlashVar = function(key, value) {
|
||||
this.flashVars_.set(key, value);
|
||||
return this;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Sets flash variables. You can either pass a Map of key->value pairs or you
|
||||
* can pass a key, value pair to set a specific variable.
|
||||
*
|
||||
* TODO(user, martino): Get rid of this method.
|
||||
*
|
||||
* @deprecated Use {@link #addFlashVars} or {@link #setFlashVar} instead.
|
||||
* @param {goog.structs.Map|Object|string} flashVar A map of variables (given
|
||||
* as a goog.structs.Map or an Object literal) or a key to the optional
|
||||
* {@code opt_value}.
|
||||
* @param {string=} opt_value The optional value for the flashVar key.
|
||||
* @return {!goog.ui.media.FlashObject} The flash object instance for chaining.
|
||||
*/
|
||||
goog.ui.media.FlashObject.prototype.setFlashVars = function(flashVar,
|
||||
opt_value) {
|
||||
if (flashVar instanceof goog.structs.Map ||
|
||||
goog.typeOf(flashVar) == 'object') {
|
||||
this.addFlashVars(/**@type {!goog.structs.Map|!Object}*/(flashVar));
|
||||
} else {
|
||||
goog.asserts.assert(goog.isString(flashVar) && goog.isDef(opt_value),
|
||||
'Invalid argument(s)');
|
||||
this.setFlashVar(/**@type {string}*/(flashVar),
|
||||
/**@type {string}*/(opt_value));
|
||||
}
|
||||
return this;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @return {goog.structs.Map} The current flash variables.
|
||||
*/
|
||||
goog.ui.media.FlashObject.prototype.getFlashVars = function() {
|
||||
return this.flashVars_;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Sets the background color of the movie.
|
||||
*
|
||||
* @param {string} color The new color to be set.
|
||||
* @return {!goog.ui.media.FlashObject} The flash object instance for chaining.
|
||||
*/
|
||||
goog.ui.media.FlashObject.prototype.setBackgroundColor = function(color) {
|
||||
this.backgroundColor_ = color;
|
||||
return this;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @return {string} The background color of the movie.
|
||||
*/
|
||||
goog.ui.media.FlashObject.prototype.getBackgroundColor = function() {
|
||||
return this.backgroundColor_;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Sets the allowScriptAccess setting of the movie.
|
||||
*
|
||||
* @param {string} value The new value to be set.
|
||||
* @return {!goog.ui.media.FlashObject} The flash object instance for chaining.
|
||||
*/
|
||||
goog.ui.media.FlashObject.prototype.setAllowScriptAccess = function(value) {
|
||||
this.allowScriptAccess_ = value;
|
||||
return this;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @return {string} The allowScriptAccess setting color of the movie.
|
||||
*/
|
||||
goog.ui.media.FlashObject.prototype.getAllowScriptAccess = function() {
|
||||
return this.allowScriptAccess_;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Sets the width and height of the movie.
|
||||
*
|
||||
* @param {number|string} width The width of the movie.
|
||||
* @param {number|string} height The height of the movie.
|
||||
* @return {!goog.ui.media.FlashObject} The flash object instance for chaining.
|
||||
*/
|
||||
goog.ui.media.FlashObject.prototype.setSize = function(width, height) {
|
||||
this.width_ = goog.isString(width) ? width : Math.round(width) + 'px';
|
||||
this.height_ = goog.isString(height) ? height : Math.round(height) + 'px';
|
||||
if (this.getElement()) {
|
||||
goog.style.setSize(this.getFlashElement(), this.width_, this.height_);
|
||||
}
|
||||
return this;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @return {?string} The flash required version.
|
||||
*/
|
||||
goog.ui.media.FlashObject.prototype.getRequiredVersion = function() {
|
||||
return this.requiredVersion_;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Sets the minimum flash required version.
|
||||
*
|
||||
* @param {?string} version The minimum required version for this movie to work,
|
||||
* or null if you want to unset it.
|
||||
* @return {!goog.ui.media.FlashObject} The flash object instance for chaining.
|
||||
*/
|
||||
goog.ui.media.FlashObject.prototype.setRequiredVersion = function(version) {
|
||||
this.requiredVersion_ = version;
|
||||
return this;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Returns whether this SWF has a minimum required flash version.
|
||||
*
|
||||
* @return {boolean} Whether a required version was set or not.
|
||||
*/
|
||||
goog.ui.media.FlashObject.prototype.hasRequiredVersion = function() {
|
||||
return this.requiredVersion_ != null;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Writes the Flash embedding {@code HTMLObjectElement} to this components root
|
||||
* element and adds listeners for all events to handle them consistently.
|
||||
* @override
|
||||
*/
|
||||
goog.ui.media.FlashObject.prototype.enterDocument = function() {
|
||||
goog.ui.media.FlashObject.superClass_.enterDocument.call(this);
|
||||
|
||||
// The SWF tag must be written after this component's element is appended to
|
||||
// the DOM. Otherwise Flash's ExternalInterface is broken in IE.
|
||||
goog.dom.safe.setInnerHtml(
|
||||
/** @type {!Element} */ (this.getElement()), this.createSwfTag_());
|
||||
if (this.width_ && this.height_) {
|
||||
this.setSize(this.width_, this.height_);
|
||||
}
|
||||
|
||||
// Sinks all the events on the bubble phase.
|
||||
//
|
||||
// Flash plugins propagates events from/to the plugin to the browser
|
||||
// inconsistently:
|
||||
//
|
||||
// 1) FF2 + linux: the flash plugin will stop the propagation of all events
|
||||
// from the plugin to the browser.
|
||||
// 2) FF3 + mac: the flash plugin will propagate events on the <embed> object
|
||||
// but that will get propagated to its parents.
|
||||
// 3) Safari 3.1.1 + mac: the flash plugin will propagate the event to the
|
||||
// <object> tag that event will propagate to its parents.
|
||||
// 4) IE7 + windows: the flash plugin will eat all events, not propagating
|
||||
// anything to the javascript.
|
||||
// 5) Chrome + windows: the flash plugin will eat all events, not propagating
|
||||
// anything to the javascript.
|
||||
//
|
||||
// To overcome this inconsistency, all events from/to the plugin are sinked,
|
||||
// since you can't assume that the events will be propagated.
|
||||
//
|
||||
// NOTE(user): we only sink events on the bubbling phase, since there are no
|
||||
// inexpensive/scalable way to stop events on the capturing phase unless we
|
||||
// added an event listener on the document for each flash object.
|
||||
this.eventHandler_.listen(
|
||||
this.getElement(),
|
||||
goog.object.getValues(goog.events.EventType),
|
||||
goog.events.Event.stopPropagation);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Creates the DOM structure.
|
||||
*
|
||||
* @override
|
||||
*/
|
||||
goog.ui.media.FlashObject.prototype.createDom = function() {
|
||||
if (this.hasRequiredVersion() &&
|
||||
!goog.userAgent.flash.isVersion(
|
||||
/** @type {string} */ (this.getRequiredVersion()))) {
|
||||
goog.log.warning(this.logger_, 'Required flash version not found:' +
|
||||
this.getRequiredVersion());
|
||||
throw Error(goog.ui.Component.Error.NOT_SUPPORTED);
|
||||
}
|
||||
|
||||
var element = this.getDomHelper().createElement('div');
|
||||
element.className = goog.ui.media.FlashObject.CSS_CLASS;
|
||||
this.setElementInternal(element);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Creates the HTML to embed the flash object.
|
||||
*
|
||||
* @return {!goog.html.SafeHtml} Browser appropriate HTML to add the SWF to the
|
||||
* DOM.
|
||||
* @private
|
||||
*/
|
||||
goog.ui.media.FlashObject.prototype.createSwfTag_ = function() {
|
||||
var keys = this.flashVars_.getKeys();
|
||||
var values = this.flashVars_.getValues();
|
||||
var flashVars = [];
|
||||
for (var i = 0; i < keys.length; i++) {
|
||||
var key = goog.string.urlEncode(keys[i]);
|
||||
var value = goog.string.urlEncode(values[i]);
|
||||
flashVars.push(key + '=' + value);
|
||||
}
|
||||
var flashVarsString = flashVars.join('&');
|
||||
if (goog.userAgent.IE && !goog.userAgent.isDocumentModeOrHigher(11)) {
|
||||
return this.createSwfTagOldIe_(flashVarsString);
|
||||
} else {
|
||||
return this.createSwfTagModern_(flashVarsString);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Creates the HTML to embed the flash object for IE>=11 and other browsers.
|
||||
*
|
||||
* @param {string} flashVars The value of the FlashVars attribute.
|
||||
* @return {!goog.html.SafeHtml} Browser appropriate HTML to add the SWF to the
|
||||
* DOM.
|
||||
* @private
|
||||
*/
|
||||
goog.ui.media.FlashObject.prototype.createSwfTagModern_ = function(flashVars) {
|
||||
return goog.html.flash.createEmbed(
|
||||
this.flashUrl_,
|
||||
{
|
||||
'AllowScriptAccess': this.allowScriptAccess_,
|
||||
'allowFullScreen': 'true',
|
||||
'allowNetworking': 'all',
|
||||
'bgcolor': this.backgroundColor_,
|
||||
'class': goog.ui.media.FlashObject.FLASH_CSS_CLASS,
|
||||
'FlashVars': flashVars,
|
||||
'id': this.getId(),
|
||||
'name': this.getId(),
|
||||
'quality': 'high',
|
||||
'SeamlessTabbing': 'false',
|
||||
'wmode': this.wmode_
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Creates the HTML to embed the flash object for IE<11.
|
||||
*
|
||||
* @param {string} flashVars The value of the FlashVars attribute.
|
||||
* @return {!goog.html.SafeHtml} Browser appropriate HTML to add the SWF to the
|
||||
* DOM.
|
||||
* @private
|
||||
*/
|
||||
goog.ui.media.FlashObject.prototype.createSwfTagOldIe_ = function(flashVars) {
|
||||
return goog.html.flash.createObjectForOldIe(
|
||||
this.flashUrl_,
|
||||
{
|
||||
'allowFullScreen': 'true',
|
||||
'AllowScriptAccess': this.allowScriptAccess_,
|
||||
'allowNetworking': 'all',
|
||||
'bgcolor': this.backgroundColor_,
|
||||
'FlashVars': flashVars,
|
||||
'quality': 'high',
|
||||
'SeamlessTabbing': 'false',
|
||||
'wmode': this.wmode_
|
||||
},
|
||||
{
|
||||
'class': goog.ui.media.FlashObject.FLASH_CSS_CLASS,
|
||||
'id': this.getId(),
|
||||
'name': this.getId()
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @return {HTMLObjectElement} The flash element or null if the element can't
|
||||
* be found.
|
||||
*/
|
||||
goog.ui.media.FlashObject.prototype.getFlashElement = function() {
|
||||
return /** @type {HTMLObjectElement} */(this.getElement() ?
|
||||
this.getElement().firstChild : null);
|
||||
};
|
||||
|
||||
|
||||
/** @override */
|
||||
goog.ui.media.FlashObject.prototype.disposeInternal = function() {
|
||||
goog.ui.media.FlashObject.superClass_.disposeInternal.call(this);
|
||||
this.flashVars_ = null;
|
||||
|
||||
this.eventHandler_.dispose();
|
||||
this.eventHandler_ = null;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @return {boolean} whether the SWF has finished loading or not.
|
||||
*/
|
||||
goog.ui.media.FlashObject.prototype.isLoaded = function() {
|
||||
if (!this.isInDocument() || !this.getElement()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this.getFlashElement().readyState &&
|
||||
this.getFlashElement().readyState ==
|
||||
goog.ui.media.FlashObject.SwfReadyStates_.COMPLETE) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (this.getFlashElement().PercentLoaded &&
|
||||
this.getFlashElement().PercentLoaded() == 100) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
@@ -0,0 +1,24 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<!--
|
||||
Copyright 2009 The Closure Library Authors. All Rights Reserved.
|
||||
|
||||
Use of this source code is governed by the Apache License, Version 2.0.
|
||||
See the COPYING file for details.
|
||||
-->
|
||||
<!--
|
||||
-->
|
||||
<head>
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
||||
<title>
|
||||
Closure Unit Tests - goog.ui.media.FlashObject
|
||||
</title>
|
||||
<script src="../../base.js">
|
||||
</script>
|
||||
<script>
|
||||
goog.require('goog.ui.media.FlashObjectTest');
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,347 @@
|
||||
// Copyright 2009 The Closure Library Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS-IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
goog.provide('goog.ui.media.FlashObjectTest');
|
||||
goog.setTestOnly('goog.ui.media.FlashObjectTest');
|
||||
|
||||
goog.require('goog.dom');
|
||||
goog.require('goog.dom.DomHelper');
|
||||
goog.require('goog.events');
|
||||
goog.require('goog.events.Event');
|
||||
goog.require('goog.events.EventType');
|
||||
goog.require('goog.html.SafeUrl');
|
||||
goog.require('goog.testing.MockControl');
|
||||
goog.require('goog.testing.events');
|
||||
goog.require('goog.testing.jsunit');
|
||||
goog.require('goog.ui.media.FlashObject');
|
||||
goog.require('goog.userAgent');
|
||||
|
||||
|
||||
var FLASH_URL = 'http://www.youtube.com/v/RbI7cCp0v6w&hl=en&fs=1';
|
||||
var control = new goog.testing.MockControl();
|
||||
var domHelper = control.createLooseMock(goog.dom.DomHelper);
|
||||
// TODO(user): mocking window.document throws exceptions in FF2. find out how
|
||||
// to mock it.
|
||||
var documentHelper = {body: control.createLooseMock(goog.dom.DomHelper)};
|
||||
var element = goog.dom.createElement('div');
|
||||
|
||||
function setUp() {
|
||||
control.$resetAll();
|
||||
domHelper.getDocument().$returns(documentHelper).$anyTimes();
|
||||
domHelper.createElement('div').$returns(element).$anyTimes();
|
||||
documentHelper.body.appendChild(element).$anyTimes();
|
||||
}
|
||||
|
||||
function tearDown() {
|
||||
control.$verifyAll();
|
||||
}
|
||||
|
||||
function getFlashVarsFromElement(flash) {
|
||||
var el = flash.getFlashElement();
|
||||
|
||||
// This should work in everything except IE:
|
||||
if (el.hasAttribute && el.hasAttribute('flashvars'))
|
||||
return el.getAttribute('flashvars');
|
||||
|
||||
// For IE: find and return the value of the correct param element:
|
||||
el = el.firstChild;
|
||||
while (el) {
|
||||
if (el.name == 'FlashVars') {
|
||||
return el.value;
|
||||
}
|
||||
el = el.nextSibling;
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
function testInstantiationAndRendering() {
|
||||
control.$replayAll();
|
||||
|
||||
var flash = new goog.ui.media.FlashObject(FLASH_URL, domHelper);
|
||||
flash.render();
|
||||
flash.dispose();
|
||||
}
|
||||
|
||||
function testRenderedWithCorrectAttributes() {
|
||||
if (goog.userAgent.IE && !goog.userAgent.isDocumentModeOrHigher(11)) {
|
||||
return;
|
||||
}
|
||||
|
||||
control.$replayAll();
|
||||
|
||||
var flash = new goog.ui.media.FlashObject(FLASH_URL, domHelper);
|
||||
flash.setAllowScriptAccess('allowScriptAccess');
|
||||
flash.setBackgroundColor('backgroundColor');
|
||||
flash.setId('id');
|
||||
flash.setFlashVars({'k1': 'v1', 'k2': 'v2'});
|
||||
flash.setWmode('wmode');
|
||||
flash.render();
|
||||
|
||||
var el = flash.getFlashElement();
|
||||
assertEquals('true', el.getAttribute('allowFullScreen'));
|
||||
assertEquals('all', el.getAttribute('allowNetworking'));
|
||||
assertEquals('allowScriptAccess', el.getAttribute('allowScriptAccess'));
|
||||
assertEquals(
|
||||
goog.ui.media.FlashObject.FLASH_CSS_CLASS, el.getAttribute('class'));
|
||||
assertEquals('k1=v1&k2=v2', el.getAttribute('FlashVars'));
|
||||
assertEquals('id', el.getAttribute('id'));
|
||||
assertEquals('id', el.getAttribute('name'));
|
||||
assertEquals('https://www.macromedia.com/go/getflashplayer',
|
||||
el.getAttribute('pluginspage'));
|
||||
assertEquals('high', el.getAttribute('quality'));
|
||||
assertEquals('false', el.getAttribute('SeamlessTabbing'));
|
||||
assertEquals(FLASH_URL, el.getAttribute('src'));
|
||||
assertEquals('application/x-shockwave-flash',
|
||||
el.getAttribute('type'));
|
||||
assertEquals('wmode', el.getAttribute('wmode'));
|
||||
}
|
||||
|
||||
function testRenderedWithCorrectAttributesOldIe() {
|
||||
if (!goog.userAgent.IE || goog.userAgent.isDocumentModeOrHigher(11)) {
|
||||
return;
|
||||
}
|
||||
|
||||
control.$replayAll();
|
||||
|
||||
var flash = new goog.ui.media.FlashObject(FLASH_URL, domHelper);
|
||||
flash.setAllowScriptAccess('allowScriptAccess');
|
||||
flash.setBackgroundColor('backgroundColor');
|
||||
flash.setId('id');
|
||||
flash.setFlashVars({'k1': 'v1', 'k2': 'v2'});
|
||||
flash.setWmode('wmode');
|
||||
flash.render();
|
||||
|
||||
var el = flash.getFlashElement();
|
||||
assertEquals('class',
|
||||
goog.ui.media.FlashObject.FLASH_CSS_CLASS, el.getAttribute('class'));
|
||||
assertEquals('clsid:d27cdb6e-ae6d-11cf-96b8-444553540000',
|
||||
el.getAttribute('classid'));
|
||||
assertEquals('id', 'id', el.getAttribute('id'));
|
||||
assertEquals('name', 'id', el.getAttribute('name'));
|
||||
|
||||
assertContainsParam(el, 'allowFullScreen', 'true');
|
||||
assertContainsParam(el, 'allowNetworking', 'all');
|
||||
assertContainsParam(el, 'AllowScriptAccess', 'allowScriptAccess');
|
||||
assertContainsParam(el, 'bgcolor', 'backgroundColor');
|
||||
assertContainsParam(el, 'FlashVars', 'FlashVars');
|
||||
assertContainsParam(el, 'movie', FLASH_URL);
|
||||
assertContainsParam(el, 'quality', 'high');
|
||||
assertContainsParam(el, 'SeamlessTabbing', 'false');
|
||||
assertContainsParam(el, 'wmode', 'wmode');
|
||||
|
||||
}
|
||||
|
||||
function testUrlIsSanitized() {
|
||||
if (goog.userAgent.IE && !goog.userAgent.isDocumentModeOrHigher(11)) {
|
||||
return;
|
||||
}
|
||||
|
||||
control.$replayAll();
|
||||
|
||||
var flash = new goog.ui.media.FlashObject('javascript:evil', domHelper);
|
||||
flash.render();
|
||||
var el = flash.getFlashElement();
|
||||
|
||||
assertEquals(goog.html.SafeUrl.INNOCUOUS_STRING, el.getAttribute('src'));
|
||||
}
|
||||
|
||||
function testUrlIsSanitizedOldIe() {
|
||||
if (!goog.userAgent.IE || goog.userAgent.isDocumentModeOrHigher(11)) {
|
||||
return;
|
||||
}
|
||||
|
||||
control.$replayAll();
|
||||
|
||||
var flash = new goog.ui.media.FlashObject('javascript:evil', domHelper);
|
||||
flash.render();
|
||||
var el = flash.getFlashElement();
|
||||
|
||||
assertContainsParam(el, 'movie', goog.html.SafeUrl.INNOCUOUS_STRING);
|
||||
}
|
||||
|
||||
function assertContainsParam(element, expectedName, expectedValue) {
|
||||
var failureMsg = 'Expected param with name \"' + expectedName +
|
||||
'\" and value \"' + expectedValue + '\". Not found in child nodes: ' +
|
||||
element.innerHTML;
|
||||
for (var i = 0; i < element.childNodes.length; i++) {
|
||||
var child = element.childNodes[i];
|
||||
var name = child.getAttribute('name');
|
||||
if (name === expectedName) {
|
||||
if (!child.getAttribute('value') === expectedValue) {
|
||||
fail(failureMsg);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
fail(failureMsg);
|
||||
}
|
||||
|
||||
function testSetFlashVar() {
|
||||
control.$replayAll();
|
||||
|
||||
var flash = new goog.ui.media.FlashObject(FLASH_URL, domHelper);
|
||||
|
||||
assertTrue(flash.getFlashVars().isEmpty());
|
||||
flash.setFlashVar('foo', 'bar');
|
||||
flash.setFlashVar('hello', 'world');
|
||||
assertFalse(flash.getFlashVars().isEmpty());
|
||||
|
||||
flash.render();
|
||||
|
||||
assertEquals('foo=bar&hello=world', getFlashVarsFromElement(flash));
|
||||
flash.dispose();
|
||||
}
|
||||
|
||||
function testAddFlashVars() {
|
||||
control.$replayAll();
|
||||
|
||||
var flash = new goog.ui.media.FlashObject(FLASH_URL, domHelper);
|
||||
|
||||
assertTrue(flash.getFlashVars().isEmpty());
|
||||
flash.addFlashVars({
|
||||
'using': 'an',
|
||||
'object': 'literal'
|
||||
});
|
||||
assertFalse(flash.getFlashVars().isEmpty());
|
||||
|
||||
flash.render();
|
||||
|
||||
assertEquals('using=an&object=literal', getFlashVarsFromElement(flash));
|
||||
flash.dispose();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @deprecated Remove once setFlashVars is removed.
|
||||
*/
|
||||
function testSetFlashVarsUsingFalseAsTheValue() {
|
||||
control.$replayAll();
|
||||
|
||||
var flash = new goog.ui.media.FlashObject(FLASH_URL, domHelper);
|
||||
|
||||
assertTrue(flash.getFlashVars().isEmpty());
|
||||
flash.setFlashVars('beEvil', false);
|
||||
assertFalse(flash.getFlashVars().isEmpty());
|
||||
|
||||
flash.render();
|
||||
|
||||
assertEquals('beEvil=false', getFlashVarsFromElement(flash));
|
||||
flash.dispose();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @deprecated Remove once setFlashVars is removed.
|
||||
*/
|
||||
function testSetFlashVarsWithWrongArgument() {
|
||||
control.$replayAll();
|
||||
|
||||
assertThrows(function() {
|
||||
var flash = new goog.ui.media.FlashObject(FLASH_URL, domHelper);
|
||||
flash.setFlashVars('foo=bar');
|
||||
flash.dispose();
|
||||
});
|
||||
}
|
||||
|
||||
function testSetFlashVarUrlEncoding() {
|
||||
control.$replayAll();
|
||||
|
||||
var flash = new goog.ui.media.FlashObject(FLASH_URL, domHelper);
|
||||
flash.setFlashVar('foo', 'bar and some extra spaces');
|
||||
flash.render();
|
||||
assertEquals('foo=bar%20and%20some%20extra%20spaces',
|
||||
getFlashVarsFromElement(flash));
|
||||
flash.dispose();
|
||||
}
|
||||
|
||||
function testThrowsRequiredVersionOfFlashNotAvailable() {
|
||||
control.$replayAll();
|
||||
|
||||
var flash = new goog.ui.media.FlashObject(FLASH_URL, domHelper);
|
||||
flash.setRequiredVersion('999.999.999');
|
||||
|
||||
assertTrue(flash.hasRequiredVersion());
|
||||
|
||||
assertThrows(function() {
|
||||
flash.render();
|
||||
});
|
||||
|
||||
flash.dispose();
|
||||
}
|
||||
|
||||
function testIsLoadedAfterDispose() {
|
||||
control.$replayAll();
|
||||
|
||||
var flash = new goog.ui.media.FlashObject(FLASH_URL, domHelper);
|
||||
flash.render();
|
||||
// TODO(user): find out a way to test the loadness of flash movies on
|
||||
// asynchronous tests. if debugger; is left here, the test pass. if removed
|
||||
// the test fails. that happens because flash needs some time to be
|
||||
// considered loaded, after flash.render() is called (like img.src i guess).
|
||||
//debugger;
|
||||
//assertTrue(flash.isLoaded());
|
||||
flash.dispose();
|
||||
assertFalse(flash.isLoaded());
|
||||
}
|
||||
|
||||
function testXssAttacks() {
|
||||
control.$replayAll();
|
||||
|
||||
called = false;
|
||||
var injection = '' +
|
||||
'">' +
|
||||
'</embed>' +
|
||||
'<script>called = true; // evil arbitrary js injected here<\/script>' +
|
||||
'<embed src=""';
|
||||
var flash = new goog.ui.media.FlashObject(injection, domHelper);
|
||||
flash.render();
|
||||
// Makes sure FlashObject html escapes user input.
|
||||
// NOTE(user): this test fails if the URL is not HTML escaped, showing that
|
||||
// html escaping is necessary to avoid attacks.
|
||||
assertFalse(called);
|
||||
}
|
||||
|
||||
function testPropagatesEventsConsistently() {
|
||||
var event = control.createLooseMock(goog.events.Event);
|
||||
|
||||
// we expect any event to have its propagation stopped.
|
||||
event.stopPropagation();
|
||||
|
||||
control.$replayAll();
|
||||
|
||||
var flash = new goog.ui.media.FlashObject(FLASH_URL, domHelper);
|
||||
flash.render();
|
||||
event.target = flash.getElement();
|
||||
event.type = goog.events.EventType.CLICK;
|
||||
goog.testing.events.fireBrowserEvent(event);
|
||||
flash.dispose();
|
||||
}
|
||||
|
||||
function testEventsGetsSinked() {
|
||||
var called = false;
|
||||
var flash = new goog.ui.media.FlashObject(FLASH_URL);
|
||||
var parent = goog.dom.createElement('div');
|
||||
flash.render(parent);
|
||||
|
||||
goog.events.listen(parent, goog.events.EventType.CLICK, function(e) {
|
||||
called = true;
|
||||
});
|
||||
|
||||
assertFalse(called);
|
||||
|
||||
goog.testing.events.fireClickSequence(flash.getElement());
|
||||
|
||||
assertFalse(called);
|
||||
flash.dispose();
|
||||
}
|
||||
@@ -0,0 +1,314 @@
|
||||
// Copyright 2009 The Closure Library Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS-IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
|
||||
/**
|
||||
* @fileoverview provides a reusable FlickrSet photo UI component given a public
|
||||
* FlickrSetModel.
|
||||
*
|
||||
* goog.ui.media.FlickrSet is actually a {@link goog.ui.ControlRenderer}, a
|
||||
* stateless class - that could/should be used as a Singleton with the static
|
||||
* method {@code goog.ui.media.FlickrSet.getInstance} -, that knows how to
|
||||
* render Flickr sets. It is designed to be used with a {@link goog.ui.Control},
|
||||
* which will actually control the media renderer and provide the
|
||||
* {@link goog.ui.Component} base. This design guarantees that all different
|
||||
* types of medias will behave alike but will look different.
|
||||
*
|
||||
* goog.ui.media.FlickrSet expects a {@code goog.ui.media.FlickrSetModel} on
|
||||
* {@code goog.ui.Control.getModel} as data models, and renders a flash object
|
||||
* that will show the contents of that set.
|
||||
*
|
||||
* Example of usage:
|
||||
*
|
||||
* <pre>
|
||||
* var flickrSet = goog.ui.media.FlickrSetModel.newInstance(flickrSetUrl);
|
||||
* goog.ui.media.FlickrSet.newControl(flickrSet).render();
|
||||
* </pre>
|
||||
*
|
||||
* FlickrSet medias currently support the following states:
|
||||
*
|
||||
* <ul>
|
||||
* <li> {@link goog.ui.Component.State.DISABLED}: shows 'flash not available'
|
||||
* <li> {@link goog.ui.Component.State.HOVER}: mouse cursor is over the video
|
||||
* <li> {@link goog.ui.Component.State.SELECTED}: flash video is shown
|
||||
* </ul>
|
||||
*
|
||||
* Which can be accessed by
|
||||
* <pre>
|
||||
* video.setEnabled(true);
|
||||
* video.setHighlighted(true);
|
||||
* video.setSelected(true);
|
||||
* </pre>
|
||||
*
|
||||
*
|
||||
* @supported IE6, FF2+, Safari. Requires flash to actually work.
|
||||
*
|
||||
* TODO(user): Support non flash users. Maybe show a link to the Flick set,
|
||||
* or fetch the data and rendering it using javascript (instead of a broken
|
||||
* 'You need to install flash' message).
|
||||
*/
|
||||
|
||||
goog.provide('goog.ui.media.FlickrSet');
|
||||
goog.provide('goog.ui.media.FlickrSetModel');
|
||||
|
||||
goog.require('goog.html.TrustedResourceUrl');
|
||||
goog.require('goog.string.Const');
|
||||
goog.require('goog.ui.media.FlashObject');
|
||||
goog.require('goog.ui.media.Media');
|
||||
goog.require('goog.ui.media.MediaModel');
|
||||
goog.require('goog.ui.media.MediaRenderer');
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Subclasses a goog.ui.media.MediaRenderer to provide a FlickrSet specific
|
||||
* media renderer.
|
||||
*
|
||||
* This class knows how to parse FlickrSet URLs, and render the DOM structure
|
||||
* of flickr set players. This class is meant to be used as a singleton static
|
||||
* stateless class, that takes {@code goog.ui.media.Media} instances and renders
|
||||
* it. It expects {@code goog.ui.media.Media.getModel} to return a well formed,
|
||||
* previously constructed, set id {@see goog.ui.media.FlickrSet.parseUrl},
|
||||
* which is the data model this renderer will use to construct the DOM
|
||||
* structure. {@see goog.ui.media.FlickrSet.newControl} for a example of
|
||||
* constructing a control with this renderer.
|
||||
*
|
||||
* This design is patterned after
|
||||
* http://go/closure_control_subclassing
|
||||
*
|
||||
* It uses {@link goog.ui.media.FlashObject} to embed the flash object.
|
||||
*
|
||||
* @constructor
|
||||
* @extends {goog.ui.media.MediaRenderer}
|
||||
* @final
|
||||
*/
|
||||
goog.ui.media.FlickrSet = function() {
|
||||
goog.ui.media.MediaRenderer.call(this);
|
||||
};
|
||||
goog.inherits(goog.ui.media.FlickrSet, goog.ui.media.MediaRenderer);
|
||||
goog.addSingletonGetter(goog.ui.media.FlickrSet);
|
||||
|
||||
|
||||
/**
|
||||
* Default CSS class to be applied to the root element of components rendered
|
||||
* by this renderer.
|
||||
*
|
||||
* @type {string}
|
||||
*/
|
||||
goog.ui.media.FlickrSet.CSS_CLASS = goog.getCssName('goog-ui-media-flickrset');
|
||||
|
||||
|
||||
/**
|
||||
* Flash player URL. Uses Flickr's flash player by default.
|
||||
*
|
||||
* @type {!goog.html.TrustedResourceUrl}
|
||||
* @private
|
||||
*/
|
||||
goog.ui.media.FlickrSet.flashUrl_ = goog.html.TrustedResourceUrl.fromConstant(
|
||||
goog.string.Const.from(
|
||||
'http://www.flickr.com/apps/slideshow/show.swf?v=63961'));
|
||||
|
||||
|
||||
/**
|
||||
* A static convenient method to construct a goog.ui.media.Media control out of
|
||||
* a FlickrSet URL. It extracts the set id information on the URL, sets it
|
||||
* as the data model goog.ui.media.FlickrSet renderer uses, sets the states
|
||||
* supported by the renderer, and returns a Control that binds everything
|
||||
* together. This is what you should be using for constructing FlickrSet videos,
|
||||
* except if you need more fine control over the configuration.
|
||||
*
|
||||
* @param {goog.ui.media.FlickrSetModel} dataModel The Flickr Set data model.
|
||||
* @param {goog.dom.DomHelper=} opt_domHelper Optional DOM helper, used for
|
||||
* document interaction.
|
||||
* @return {!goog.ui.media.Media} A Control binded to the FlickrSet renderer.
|
||||
* @throws exception in case {@code flickrSetUrl} is an invalid flickr set URL.
|
||||
* TODO(user): use {@link goog.ui.media.MediaModel} once it is checked in.
|
||||
*/
|
||||
goog.ui.media.FlickrSet.newControl = function(dataModel, opt_domHelper) {
|
||||
var control = new goog.ui.media.Media(
|
||||
dataModel, goog.ui.media.FlickrSet.getInstance(), opt_domHelper);
|
||||
control.setSelected(true);
|
||||
return control;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* A static method that sets which flash URL this class should use. Use this if
|
||||
* you want to host your own flash flickr player.
|
||||
*
|
||||
* @param {!goog.html.TrustedResourceUrl} flashUrl The URL of the flash flickr
|
||||
* player.
|
||||
*/
|
||||
goog.ui.media.FlickrSet.setFlashUrl = function(flashUrl) {
|
||||
goog.ui.media.FlickrSet.flashUrl_ = flashUrl;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Creates the initial DOM structure of the flickr set, which is basically a
|
||||
* the flash object pointing to a flickr set player.
|
||||
*
|
||||
* @param {goog.ui.Control} c The media control.
|
||||
* @return {!Element} The DOM structure that represents this control.
|
||||
* @override
|
||||
*/
|
||||
goog.ui.media.FlickrSet.prototype.createDom = function(c) {
|
||||
var control = /** @type {goog.ui.media.Media} */ (c);
|
||||
var div = goog.ui.media.FlickrSet.superClass_.createDom.call(this, control);
|
||||
|
||||
var model =
|
||||
/** @type {goog.ui.media.FlickrSetModel} */ (control.getDataModel());
|
||||
|
||||
// TODO(user): find out what is the policy about hosting this SWF. figure out
|
||||
// if it works over https.
|
||||
var flash = new goog.ui.media.FlashObject(
|
||||
model.getPlayer().getTrustedResourceUrl(),
|
||||
control.getDomHelper());
|
||||
flash.addFlashVars(model.getPlayer().getVars());
|
||||
flash.render(div);
|
||||
|
||||
return div;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Returns the CSS class to be applied to the root element of components
|
||||
* rendered using this renderer.
|
||||
* @return {string} Renderer-specific CSS class.
|
||||
* @override
|
||||
*/
|
||||
goog.ui.media.FlickrSet.prototype.getCssClass = function() {
|
||||
return goog.ui.media.FlickrSet.CSS_CLASS;
|
||||
};
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* The {@code goog.ui.media.FlickrAlbum} media data model. It stores a required
|
||||
* {@code userId} and {@code setId} fields, sets the flickr Set URL, and
|
||||
* allows a few optional parameters.
|
||||
*
|
||||
* @param {string} userId The flickr userId associated with this set.
|
||||
* @param {string} setId The flickr setId associated with this set.
|
||||
* @param {string=} opt_caption An optional caption of the flickr set.
|
||||
* @param {string=} opt_description An optional description of the flickr set.
|
||||
* @constructor
|
||||
* @extends {goog.ui.media.MediaModel}
|
||||
* @final
|
||||
*/
|
||||
goog.ui.media.FlickrSetModel = function(userId,
|
||||
setId,
|
||||
opt_caption,
|
||||
opt_description) {
|
||||
goog.ui.media.MediaModel.call(
|
||||
this,
|
||||
goog.ui.media.FlickrSetModel.buildUrl(userId, setId),
|
||||
opt_caption,
|
||||
opt_description,
|
||||
goog.ui.media.MediaModel.MimeType.FLASH);
|
||||
|
||||
/**
|
||||
* The Flickr user id.
|
||||
* @type {string}
|
||||
* @private
|
||||
*/
|
||||
this.userId_ = userId;
|
||||
|
||||
/**
|
||||
* The Flickr set id.
|
||||
* @type {string}
|
||||
* @private
|
||||
*/
|
||||
this.setId_ = setId;
|
||||
|
||||
var flashVars = {
|
||||
'offsite': 'true',
|
||||
'lang': 'en',
|
||||
'page_show_url': '/photos/' + userId + '/sets/' + setId + '/show/',
|
||||
'page_show_back_url': '/photos/' + userId + '/sets/' + setId,
|
||||
'set_id': setId
|
||||
};
|
||||
|
||||
var player = new goog.ui.media.MediaModel.Player(
|
||||
goog.ui.media.FlickrSet.flashUrl_, flashVars);
|
||||
|
||||
this.setPlayer(player);
|
||||
};
|
||||
goog.inherits(goog.ui.media.FlickrSetModel, goog.ui.media.MediaModel);
|
||||
|
||||
|
||||
/**
|
||||
* Regular expression used to extract the username and set id out of the flickr
|
||||
* URLs.
|
||||
*
|
||||
* Copied from http://go/markdownlite.js and {@link FlickrExtractor.xml}.
|
||||
*
|
||||
* @type {RegExp}
|
||||
* @private
|
||||
* @const
|
||||
*/
|
||||
goog.ui.media.FlickrSetModel.MATCHER_ =
|
||||
/(?:http:\/\/)?(?:www\.)?flickr\.com\/(?:photos\/([\d\w@\-]+)\/sets\/(\d+))\/?/i;
|
||||
|
||||
|
||||
/**
|
||||
* Takes a {@code flickrSetUrl} and extracts the flickr username and set id.
|
||||
*
|
||||
* @param {string} flickrSetUrl A Flickr set URL.
|
||||
* @param {string=} opt_caption An optional caption of the flickr set.
|
||||
* @param {string=} opt_description An optional description of the flickr set.
|
||||
* @return {!goog.ui.media.FlickrSetModel} The data model that represents the
|
||||
* Flickr set.
|
||||
* @throws exception in case the parsing fails
|
||||
*/
|
||||
goog.ui.media.FlickrSetModel.newInstance = function(flickrSetUrl,
|
||||
opt_caption,
|
||||
opt_description) {
|
||||
if (goog.ui.media.FlickrSetModel.MATCHER_.test(flickrSetUrl)) {
|
||||
var data = goog.ui.media.FlickrSetModel.MATCHER_.exec(flickrSetUrl);
|
||||
return new goog.ui.media.FlickrSetModel(
|
||||
data[1], data[2], opt_caption, opt_description);
|
||||
}
|
||||
throw Error('failed to parse flickr url: ' + flickrSetUrl);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Takes a flickr username and set id and returns an URL.
|
||||
*
|
||||
* @param {string} userId The owner of the set.
|
||||
* @param {string} setId The set id.
|
||||
* @return {string} The URL of the set.
|
||||
*/
|
||||
goog.ui.media.FlickrSetModel.buildUrl = function(userId, setId) {
|
||||
return 'http://flickr.com/photos/' + userId + '/sets/' + setId;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Gets the Flickr user id.
|
||||
* @return {string} The Flickr user id.
|
||||
*/
|
||||
goog.ui.media.FlickrSetModel.prototype.getUserId = function() {
|
||||
return this.userId_;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Gets the Flickr set id.
|
||||
* @return {string} The Flickr set id.
|
||||
*/
|
||||
goog.ui.media.FlickrSetModel.prototype.getSetId = function() {
|
||||
return this.setId_;
|
||||
};
|
||||
@@ -0,0 +1,24 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<!--
|
||||
Copyright 2009 The Closure Library Authors. All Rights Reserved.
|
||||
|
||||
Use of this source code is governed by the Apache License, Version 2.0.
|
||||
See the COPYING file for details.
|
||||
-->
|
||||
<!--
|
||||
-->
|
||||
<head>
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
||||
<title>
|
||||
Closure Unit Tests - goog.ui.media.FlickrSet
|
||||
</title>
|
||||
<script src="../../base.js">
|
||||
</script>
|
||||
<script>
|
||||
goog.require('goog.ui.media.FlickrSetTest');
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,106 @@
|
||||
// Copyright 2009 The Closure Library Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS-IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
goog.provide('goog.ui.media.FlickrSetTest');
|
||||
goog.setTestOnly('goog.ui.media.FlickrSetTest');
|
||||
|
||||
goog.require('goog.dom');
|
||||
goog.require('goog.html.testing');
|
||||
goog.require('goog.testing.jsunit');
|
||||
goog.require('goog.ui.media.FlashObject');
|
||||
goog.require('goog.ui.media.FlickrSet');
|
||||
goog.require('goog.ui.media.FlickrSetModel');
|
||||
goog.require('goog.ui.media.Media');
|
||||
var flickr;
|
||||
var control;
|
||||
var FLICKR_USER = 'ingawalker';
|
||||
var FLICKR_SET = '72057594102831547';
|
||||
var FLICKRSET_URL =
|
||||
'http://flickr.com/photos/' + FLICKR_USER + '/sets/' + FLICKR_SET;
|
||||
var parent = goog.dom.createElement('div');
|
||||
|
||||
function setUp() {
|
||||
flickr = new goog.ui.media.FlickrSet();
|
||||
var set = new goog.ui.media.FlickrSetModel(FLICKR_USER, FLICKR_SET,
|
||||
'caption');
|
||||
control = new goog.ui.media.Media(set, flickr);
|
||||
}
|
||||
|
||||
function tearDown() {
|
||||
control.dispose();
|
||||
}
|
||||
|
||||
function testBasicRendering() {
|
||||
control.render(parent);
|
||||
var el = goog.dom.getElementsByTagNameAndClass(
|
||||
'div', goog.ui.media.FlickrSet.CSS_CLASS, parent);
|
||||
assertEquals(1, el.length);
|
||||
assertEquals(FLICKRSET_URL, control.getDataModel().getUrl());
|
||||
}
|
||||
|
||||
function testParsingUrl() {
|
||||
assertExtractsCorrectly(FLICKR_USER, FLICKR_SET, FLICKRSET_URL);
|
||||
// user id with @ sign
|
||||
assertExtractsCorrectly('30441750@N06', '7215760789302468',
|
||||
'http://flickr.com/photos/30441750@N06/sets/7215760789302468/');
|
||||
// user id with - sign
|
||||
assertExtractsCorrectly('30441750-N06', '7215760789302468',
|
||||
'http://flickr.com/photos/30441750-N06/sets/7215760789302468/');
|
||||
|
||||
var invalidUrl = 'http://invalidUrl/filename.doc';
|
||||
var e = assertThrows('', function() {
|
||||
goog.ui.media.FlickrSetModel.newInstance(invalidUrl);
|
||||
});
|
||||
assertEquals('failed to parse flickr url: ' + invalidUrl, e.message);
|
||||
}
|
||||
|
||||
function testBuildingUrl() {
|
||||
assertEquals(FLICKRSET_URL,
|
||||
goog.ui.media.FlickrSetModel.buildUrl(
|
||||
FLICKR_USER, FLICKR_SET, FLICKRSET_URL));
|
||||
}
|
||||
|
||||
function testCreatingModel() {
|
||||
var model = new goog.ui.media.FlickrSetModel(FLICKR_USER, FLICKR_SET);
|
||||
assertEquals(FLICKR_USER, model.getUserId());
|
||||
assertEquals(FLICKR_SET, model.getSetId());
|
||||
assertEquals(FLICKRSET_URL, model.getUrl());
|
||||
assertUndefined(model.getCaption());
|
||||
}
|
||||
|
||||
function testSettingWhichFlashUrlToUse() {
|
||||
goog.ui.media.FlickrSet.setFlashUrl(
|
||||
goog.html.testing.newTrustedResourceUrlForTest('http://foo'));
|
||||
assertEquals('http://foo',
|
||||
goog.ui.media.FlickrSet.flashUrl_.getTypedStringValue());
|
||||
}
|
||||
|
||||
function testCreatingDomOnInitialState() {
|
||||
control.render(parent);
|
||||
var caption = goog.dom.getElementsByTagNameAndClass(
|
||||
'div',
|
||||
goog.ui.media.FlickrSet.CSS_CLASS + '-caption',
|
||||
parent);
|
||||
assertEquals(1, caption.length);
|
||||
|
||||
var flash = goog.dom.getElementsByTagNameAndClass(
|
||||
'div', goog.ui.media.FlashObject.CSS_CLASS, parent);
|
||||
assertEquals(1, flash.length);
|
||||
}
|
||||
|
||||
function assertExtractsCorrectly(expectedUserId, expectedSetId, url) {
|
||||
var flickr = goog.ui.media.FlickrSetModel.newInstance(url);
|
||||
assertEquals('userId for ' + url, expectedUserId, flickr.getUserId());
|
||||
assertEquals('setId for ' + url, expectedSetId, flickr.getSetId());
|
||||
}
|
||||
@@ -0,0 +1,283 @@
|
||||
// Copyright 2011 The Closure Library Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS-IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
/**
|
||||
* @fileoverview provides a reusable GoogleVideo UI component given a public
|
||||
* GoogleVideo video URL.
|
||||
*
|
||||
* goog.ui.media.GoogleVideo is actually a {@link goog.ui.ControlRenderer}, a
|
||||
* stateless class - that could/should be used as a Singleton with the static
|
||||
* method {@code goog.ui.media.GoogleVideo.getInstance} -, that knows how to
|
||||
* render GoogleVideo videos. It is designed to be used with a
|
||||
* {@link goog.ui.Control}, which will actually control the media renderer and
|
||||
* provide the {@link goog.ui.Component} base. This design guarantees that all
|
||||
* different types of medias will behave alike but will look different.
|
||||
*
|
||||
* goog.ui.media.GoogleVideo expects {@code goog.ui.media.GoogleVideoModel} on
|
||||
* {@code goog.ui.Control.getModel} as data models, and renders a flash object
|
||||
* that will show the contents of that video.
|
||||
*
|
||||
* Example of usage:
|
||||
*
|
||||
* <pre>
|
||||
* var video = goog.ui.media.GoogleVideoModel.newInstance(
|
||||
* 'http://video.google.com/videoplay?docid=6698933542780842398');
|
||||
* goog.ui.media.GoogleVideo.newControl(video).render();
|
||||
* </pre>
|
||||
*
|
||||
* GoogleVideo medias currently support the following states:
|
||||
*
|
||||
* <ul>
|
||||
* <li> {@link goog.ui.Component.State.DISABLED}: shows 'flash not available'
|
||||
* <li> {@link goog.ui.Component.State.HOVER}: mouse cursor is over the video
|
||||
* <li> {@link goog.ui.Component.State.SELECTED}: flash video is shown
|
||||
* </ul>
|
||||
*
|
||||
* Which can be accessed by
|
||||
* <pre>
|
||||
* video.setEnabled(true);
|
||||
* video.setHighlighted(true);
|
||||
* video.setSelected(true);
|
||||
* </pre>
|
||||
*
|
||||
*
|
||||
* @supported IE6+, FF2+, Chrome, Safari. Requires flash to actually work.
|
||||
*/
|
||||
|
||||
|
||||
goog.provide('goog.ui.media.GoogleVideo');
|
||||
goog.provide('goog.ui.media.GoogleVideoModel');
|
||||
|
||||
goog.require('goog.string');
|
||||
goog.require('goog.ui.media.FlashObject');
|
||||
goog.require('goog.ui.media.Media');
|
||||
goog.require('goog.ui.media.MediaModel');
|
||||
goog.require('goog.ui.media.MediaRenderer');
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Subclasses a goog.ui.media.MediaRenderer to provide a GoogleVideo specific
|
||||
* media renderer.
|
||||
*
|
||||
* This class knows how to parse GoogleVideo URLs, and render the DOM structure
|
||||
* of GoogleVideo video players. This class is meant to be used as a singleton
|
||||
* static stateless class, that takes {@code goog.ui.media.Media} instances and
|
||||
* renders it. It expects {@code goog.ui.media.Media.getModel} to return a well
|
||||
* formed, previously constructed, GoogleVideo video id, which is the data model
|
||||
* this renderer will use to construct the DOM structure.
|
||||
* {@see goog.ui.media.GoogleVideo.newControl} for a example of constructing a
|
||||
* control with this renderer.
|
||||
*
|
||||
* This design is patterned after http://go/closure_control_subclassing
|
||||
*
|
||||
* It uses {@link goog.ui.media.FlashObject} to embed the flash object.
|
||||
*
|
||||
* @constructor
|
||||
* @extends {goog.ui.media.MediaRenderer}
|
||||
* @final
|
||||
*/
|
||||
goog.ui.media.GoogleVideo = function() {
|
||||
goog.ui.media.MediaRenderer.call(this);
|
||||
};
|
||||
goog.inherits(goog.ui.media.GoogleVideo, goog.ui.media.MediaRenderer);
|
||||
goog.addSingletonGetter(goog.ui.media.GoogleVideo);
|
||||
|
||||
|
||||
/**
|
||||
* A static convenient method to construct a goog.ui.media.Media control out of
|
||||
* a GoogleVideo model. It sets it as the data model goog.ui.media.GoogleVideo
|
||||
* renderer uses, sets the states supported by the renderer, and returns a
|
||||
* Control that binds everything together. This is what you should be using for
|
||||
* constructing GoogleVideo videos, except if you need finer control over the
|
||||
* configuration.
|
||||
*
|
||||
* @param {goog.ui.media.GoogleVideoModel} dataModel The GoogleVideo data model.
|
||||
* @param {goog.dom.DomHelper=} opt_domHelper Optional DOM helper, used for
|
||||
* document interaction.
|
||||
* @return {!goog.ui.media.Media} A Control binded to the GoogleVideo renderer.
|
||||
*/
|
||||
goog.ui.media.GoogleVideo.newControl = function(dataModel, opt_domHelper) {
|
||||
var control = new goog.ui.media.Media(
|
||||
dataModel,
|
||||
goog.ui.media.GoogleVideo.getInstance(),
|
||||
opt_domHelper);
|
||||
// GoogleVideo videos don't have any thumbnail for now, so we show the
|
||||
// "selected" version of the UI at the start, which is the flash player.
|
||||
control.setSelected(true);
|
||||
return control;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Default CSS class to be applied to the root element of components rendered
|
||||
* by this renderer.
|
||||
*
|
||||
* @type {string}
|
||||
*/
|
||||
goog.ui.media.GoogleVideo.CSS_CLASS =
|
||||
goog.getCssName('goog-ui-media-googlevideo');
|
||||
|
||||
|
||||
/**
|
||||
* Creates the initial DOM structure of the GoogleVideo video, which is
|
||||
* basically a the flash object pointing to a GoogleVideo video player.
|
||||
*
|
||||
* @param {goog.ui.Control} c The media control.
|
||||
* @return {!Element} The DOM structure that represents this control.
|
||||
* @override
|
||||
*/
|
||||
goog.ui.media.GoogleVideo.prototype.createDom = function(c) {
|
||||
var control = /** @type {goog.ui.media.Media} */ (c);
|
||||
var div = goog.ui.media.GoogleVideo.base(this, 'createDom', control);
|
||||
|
||||
var dataModel =
|
||||
/** @type {goog.ui.media.GoogleVideoModel} */ (control.getDataModel());
|
||||
|
||||
var flash = new goog.ui.media.FlashObject(
|
||||
dataModel.getPlayer().getTrustedResourceUrl(),
|
||||
control.getDomHelper());
|
||||
flash.render(div);
|
||||
|
||||
return div;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Returns the CSS class to be applied to the root element of components
|
||||
* rendered using this renderer.
|
||||
*
|
||||
* @return {string} Renderer-specific CSS class.
|
||||
* @override
|
||||
*/
|
||||
goog.ui.media.GoogleVideo.prototype.getCssClass = function() {
|
||||
return goog.ui.media.GoogleVideo.CSS_CLASS;
|
||||
};
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* The {@code goog.ui.media.GoogleVideo} media data model. It stores a required
|
||||
* {@code videoId} field, sets the GoogleVideo URL, and allows a few optional
|
||||
* parameters.
|
||||
*
|
||||
* @param {string} videoId The GoogleVideo video id.
|
||||
* @param {string=} opt_caption An optional caption of the GoogleVideo video.
|
||||
* @param {string=} opt_description An optional description of the GoogleVideo
|
||||
* video.
|
||||
* @param {boolean=} opt_autoplay Whether to autoplay video.
|
||||
* @constructor
|
||||
* @extends {goog.ui.media.MediaModel}
|
||||
* @final
|
||||
*/
|
||||
goog.ui.media.GoogleVideoModel = function(videoId, opt_caption, opt_description,
|
||||
opt_autoplay) {
|
||||
goog.ui.media.MediaModel.call(
|
||||
this,
|
||||
goog.ui.media.GoogleVideoModel.buildUrl(videoId),
|
||||
opt_caption,
|
||||
opt_description,
|
||||
goog.ui.media.MediaModel.MimeType.FLASH);
|
||||
|
||||
/**
|
||||
* The GoogleVideo video id.
|
||||
* @type {string}
|
||||
* @private
|
||||
*/
|
||||
this.videoId_ = videoId;
|
||||
|
||||
this.setPlayer(new goog.ui.media.MediaModel.Player(
|
||||
goog.ui.media.GoogleVideoModel.buildFlashUrl(videoId, opt_autoplay)));
|
||||
};
|
||||
goog.inherits(goog.ui.media.GoogleVideoModel, goog.ui.media.MediaModel);
|
||||
|
||||
|
||||
/**
|
||||
* Regular expression used to extract the GoogleVideo video id (docid) out of
|
||||
* GoogleVideo URLs.
|
||||
*
|
||||
* @type {RegExp}
|
||||
* @private
|
||||
* @const
|
||||
*/
|
||||
goog.ui.media.GoogleVideoModel.MATCHER_ =
|
||||
/^http:\/\/(?:www\.)?video\.google\.com\/videoplay.*[\?#]docid=(-?[0-9]+)#?$/i;
|
||||
|
||||
|
||||
/**
|
||||
* A auxiliary static method that parses a GoogleVideo URL, extracting the ID of
|
||||
* the video, and builds a GoogleVideoModel.
|
||||
*
|
||||
* @param {string} googleVideoUrl A GoogleVideo video URL.
|
||||
* @param {string=} opt_caption An optional caption of the GoogleVideo video.
|
||||
* @param {string=} opt_description An optional description of the GoogleVideo
|
||||
* video.
|
||||
* @param {boolean=} opt_autoplay Whether to autoplay video.
|
||||
* @return {!goog.ui.media.GoogleVideoModel} The data model that represents the
|
||||
* GoogleVideo URL.
|
||||
* @see goog.ui.media.GoogleVideoModel.getVideoId()
|
||||
* @throws Error in case the parsing fails.
|
||||
*/
|
||||
goog.ui.media.GoogleVideoModel.newInstance = function(googleVideoUrl,
|
||||
opt_caption,
|
||||
opt_description,
|
||||
opt_autoplay) {
|
||||
if (goog.ui.media.GoogleVideoModel.MATCHER_.test(googleVideoUrl)) {
|
||||
var data = goog.ui.media.GoogleVideoModel.MATCHER_.exec(googleVideoUrl);
|
||||
return new goog.ui.media.GoogleVideoModel(
|
||||
data[1], opt_caption, opt_description, opt_autoplay);
|
||||
}
|
||||
|
||||
throw Error('failed to parse video id from GoogleVideo url: ' +
|
||||
googleVideoUrl);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* The opposite of {@code goog.ui.media.GoogleVideo.newInstance}: it takes a
|
||||
* videoId and returns a GoogleVideo URL.
|
||||
*
|
||||
* @param {string} videoId The GoogleVideo video ID.
|
||||
* @return {string} The GoogleVideo URL.
|
||||
*/
|
||||
goog.ui.media.GoogleVideoModel.buildUrl = function(videoId) {
|
||||
return 'http://video.google.com/videoplay?docid=' +
|
||||
goog.string.urlEncode(videoId);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* An auxiliary method that builds URL of the flash movie to be embedded,
|
||||
* out of the GoogleVideo video id.
|
||||
*
|
||||
* @param {string} videoId The GoogleVideo video ID.
|
||||
* @param {boolean=} opt_autoplay Whether the flash movie should start playing
|
||||
* as soon as it is shown, or if it should show a 'play' button.
|
||||
* @return {string} The flash URL to be embedded on the page.
|
||||
*/
|
||||
goog.ui.media.GoogleVideoModel.buildFlashUrl = function(videoId, opt_autoplay) {
|
||||
var autoplay = opt_autoplay ? '&autoplay=1' : '';
|
||||
return 'http://video.google.com/googleplayer.swf?docid=' +
|
||||
goog.string.urlEncode(videoId) +
|
||||
'&hl=en&fs=true' + autoplay;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Gets the GoogleVideo video id.
|
||||
* @return {string} The GoogleVideo video id.
|
||||
*/
|
||||
goog.ui.media.GoogleVideoModel.prototype.getVideoId = function() {
|
||||
return this.videoId_;
|
||||
};
|
||||
@@ -0,0 +1,24 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<!--
|
||||
Copyright 2011 The Closure Library Authors. All Rights Reserved.
|
||||
|
||||
Use of this source code is governed by the Apache License, Version 2.0.
|
||||
See the COPYING file for details.
|
||||
-->
|
||||
<!--
|
||||
-->
|
||||
<head>
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
||||
<title>
|
||||
Closure Unit Tests - goog.ui.media.GoogleVideo
|
||||
</title>
|
||||
<script src="../../base.js">
|
||||
</script>
|
||||
<script>
|
||||
goog.require('goog.ui.media.GoogleVideoTest');
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,94 @@
|
||||
// Copyright 2011 The Closure Library Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS-IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
goog.provide('goog.ui.media.GoogleVideoTest');
|
||||
goog.setTestOnly('goog.ui.media.GoogleVideoTest');
|
||||
|
||||
goog.require('goog.dom');
|
||||
goog.require('goog.testing.jsunit');
|
||||
goog.require('goog.ui.media.FlashObject');
|
||||
goog.require('goog.ui.media.GoogleVideo');
|
||||
goog.require('goog.ui.media.GoogleVideoModel');
|
||||
goog.require('goog.ui.media.Media');
|
||||
var video;
|
||||
var control;
|
||||
var VIDEO_URL_PREFIX = 'http://video.google.com/videoplay?docid=';
|
||||
var VIDEO_ID = '7582902000166025817';
|
||||
var VIDEO_URL = VIDEO_URL_PREFIX + VIDEO_ID;
|
||||
var parent = goog.dom.createElement('div');
|
||||
|
||||
function setUp() {
|
||||
video = new goog.ui.media.GoogleVideo();
|
||||
var model = new goog.ui.media.GoogleVideoModel(VIDEO_ID, 'video caption');
|
||||
control = new goog.ui.media.Media(model, video);
|
||||
control.setSelected(true);
|
||||
}
|
||||
|
||||
function tearDown() {
|
||||
control.dispose();
|
||||
}
|
||||
|
||||
function testBasicRendering() {
|
||||
control.render(parent);
|
||||
var el = goog.dom.getElementsByTagNameAndClass(
|
||||
'div', goog.ui.media.GoogleVideo.CSS_CLASS, parent);
|
||||
assertEquals(1, el.length);
|
||||
assertEquals(VIDEO_URL, control.getDataModel().getUrl());
|
||||
}
|
||||
|
||||
function testParsingUrl() {
|
||||
assertExtractsCorrectly(VIDEO_ID, VIDEO_URL);
|
||||
// Test a url with # at the end.
|
||||
assertExtractsCorrectly(VIDEO_ID, VIDEO_URL + '#');
|
||||
// Test a url with a negative docid.
|
||||
assertExtractsCorrectly('-123', VIDEO_URL_PREFIX + '-123');
|
||||
// Test a url with two docids. The valid one is the second.
|
||||
assertExtractsCorrectly('123', VIDEO_URL + '#docid=123');
|
||||
|
||||
var invalidUrl = 'http://invalidUrl/filename.doc';
|
||||
var e = assertThrows('parser expects a well formed URL', function() {
|
||||
goog.ui.media.GoogleVideoModel.newInstance(invalidUrl);
|
||||
});
|
||||
assertEquals('failed to parse video id from GoogleVideo url: ' + invalidUrl,
|
||||
e.message);
|
||||
}
|
||||
|
||||
function testBuildingUrl() {
|
||||
assertEquals(VIDEO_URL, goog.ui.media.GoogleVideoModel.buildUrl(VIDEO_ID));
|
||||
}
|
||||
|
||||
function testCreatingModel() {
|
||||
var model = new goog.ui.media.GoogleVideoModel(VIDEO_ID);
|
||||
assertEquals(VIDEO_ID, model.getVideoId());
|
||||
assertEquals(VIDEO_URL, model.getUrl());
|
||||
assertUndefined(model.getCaption());
|
||||
}
|
||||
|
||||
function testCreatingDomOnInitialState() {
|
||||
control.render(parent);
|
||||
var caption = goog.dom.getElementsByTagNameAndClass(
|
||||
'div',
|
||||
goog.ui.media.GoogleVideo.CSS_CLASS + '-caption',
|
||||
parent);
|
||||
assertEquals(1, caption.length);
|
||||
|
||||
var flash = goog.dom.getElementsByTagNameAndClass(
|
||||
'div', goog.ui.media.FlashObject.CSS_CLASS, parent);
|
||||
assertEquals(1, flash.length);
|
||||
}
|
||||
|
||||
function assertExtractsCorrectly(expectedVideoId, url) {
|
||||
var model = goog.ui.media.GoogleVideoModel.newInstance(url);
|
||||
assertEquals('Video id for ' + url, expectedVideoId, model.getVideoId());
|
||||
}
|
||||
@@ -0,0 +1,288 @@
|
||||
// Copyright 2009 The Closure Library Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS-IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
/**
|
||||
* @fileoverview Provides the base goog.ui.Control and goog.ui.ControlRenderer
|
||||
* for media types, as well as a media model consistent with the Yahoo Media RSS
|
||||
* specification {@link http://search.yahoo.com/mrss/}.
|
||||
*
|
||||
* The goog.ui.media.* package is basically a set of goog.ui.ControlRenderers
|
||||
* subclasses (such as goog.ui.media.Youtube, goog.ui.media.Picasa, etc) that
|
||||
* should all work with the same goog.ui.Control (goog.ui.media.Media) logic.
|
||||
*
|
||||
* This design guarantees that all different types of medias will behave alike
|
||||
* (in a base level) but will look different.
|
||||
*
|
||||
* In MVC terms, {@link goog.ui.media.Media} is the Controller,
|
||||
* {@link goog.ui.media.MediaRenderer} + CSS definitions are the View and
|
||||
* {@code goog.ui.media.MediaModel} is the data Model. Typically,
|
||||
* MediaRenderer will be subclassed to provide media specific renderers.
|
||||
* MediaRenderer subclasses are also responsible for defining the data model.
|
||||
*
|
||||
* This design is strongly patterned after:
|
||||
* http://go/closure_control_subclassing
|
||||
*
|
||||
* goog.ui.media.MediaRenderer handles the basic common ways to display media,
|
||||
* such as displaying tooltips, frames, minimize/maximize buttons, play buttons,
|
||||
* etc. Its subclasses are responsible for rendering media specific DOM
|
||||
* structures, like youtube flash players, picasa albums, etc.
|
||||
*
|
||||
* goog.ui.media.Media handles the Control of Medias, by listening to events
|
||||
* and firing the appropriate actions. It knows about the existence of captions,
|
||||
* minimize/maximize buttons, and takes all the actions needed to change states,
|
||||
* including delegating the UI actions to MediaRenderers.
|
||||
*
|
||||
* Although MediaRenderer is a base class designed to be subclassed, it can
|
||||
* be used by itself:
|
||||
*
|
||||
* <pre>
|
||||
* var renderer = new goog.ui.media.MediaRenderer();
|
||||
* var control = new goog.ui.media.Media('hello world', renderer);
|
||||
* var control.render(goog.dom.getElement('mediaHolder'));
|
||||
* </pre>
|
||||
*
|
||||
* It requires a few CSS rules to be defined, which you should use to control
|
||||
* how the component is displayed. {@link goog.ui.ControlRenderer}s is very CSS
|
||||
* intensive, which separates the UI structure (the HTML DOM elements, which is
|
||||
* created by the {@code goog.ui.media.MediaRenderer}) from the UI view (which
|
||||
* nodes are visible, which aren't, where they are positioned. These are defined
|
||||
* on the CSS rules for each state). A few examples of CSS selectors that needs
|
||||
* to be defined are:
|
||||
*
|
||||
* <ul>
|
||||
* <li>.goog-ui-media
|
||||
* <li>.goog-ui-media-hover
|
||||
* <li>.goog-ui-media-selected
|
||||
* </ul>
|
||||
*
|
||||
* If you want to have different custom renderers CSS namespaces (eg. you may
|
||||
* want to show a small thumbnail, or you may want to hide the caption, etc),
|
||||
* you can do so by using:
|
||||
*
|
||||
* <pre>
|
||||
* var renderer = goog.ui.ControlRenderer.getCustomRenderer(
|
||||
* goog.ui.media.MediaRenderer, 'my-custom-namespace');
|
||||
* var media = new goog.ui.media.Media('', renderer);
|
||||
* media.render(goog.dom.getElement('parent'));
|
||||
* </pre>
|
||||
*
|
||||
* Which will allow you to set your own .my-custom-namespace-hover,
|
||||
* .my-custom-namespace-selected CSS selectors.
|
||||
*
|
||||
* NOTE(user): it seems like an overkill to subclass goog.ui.Control instead of
|
||||
* using a factory, but we wanted to make sure we had more control over the
|
||||
* events for future media implementations. Since we intent to use it in many
|
||||
* different places, it makes sense to have a more flexible design that lets us
|
||||
* control the inner workings of goog.ui.Control.
|
||||
*
|
||||
* TODO(user): implement, as needed, the Media specific state changes UI, such
|
||||
* as minimize/maximize buttons, expand/close buttons, etc.
|
||||
*
|
||||
*/
|
||||
|
||||
goog.provide('goog.ui.media.Media');
|
||||
goog.provide('goog.ui.media.MediaRenderer');
|
||||
|
||||
goog.require('goog.asserts');
|
||||
goog.require('goog.style');
|
||||
goog.require('goog.ui.Component');
|
||||
goog.require('goog.ui.Control');
|
||||
goog.require('goog.ui.ControlRenderer');
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Provides the control mechanism of media types.
|
||||
*
|
||||
* @param {goog.ui.media.MediaModel} dataModel The data model to be used by the
|
||||
* renderer.
|
||||
* @param {goog.ui.ControlRenderer=} opt_renderer Renderer used to render or
|
||||
* decorate the component; defaults to {@link goog.ui.ControlRenderer}.
|
||||
* @param {goog.dom.DomHelper=} opt_domHelper Optional DOM helper, used for
|
||||
* document interaction.
|
||||
* @constructor
|
||||
* @extends {goog.ui.Control}
|
||||
* @final
|
||||
*/
|
||||
goog.ui.media.Media = function(dataModel, opt_renderer, opt_domHelper) {
|
||||
goog.ui.Control.call(this, null, opt_renderer, opt_domHelper);
|
||||
|
||||
// Sets up the data model.
|
||||
this.setDataModel(dataModel);
|
||||
this.setSupportedState(goog.ui.Component.State.OPENED, true);
|
||||
this.setSupportedState(goog.ui.Component.State.SELECTED, true);
|
||||
// TODO(user): had to do this to for mouseDownHandler not to
|
||||
// e.preventDefault(), because it was not allowing the event to reach the
|
||||
// flash player. figure out a better way to not e.preventDefault().
|
||||
this.setAllowTextSelection(true);
|
||||
|
||||
// Media items don't use RTL styles, so avoid accessing computed styles to
|
||||
// figure out if the control is RTL.
|
||||
this.setRightToLeft(false);
|
||||
};
|
||||
goog.inherits(goog.ui.media.Media, goog.ui.Control);
|
||||
|
||||
|
||||
/**
|
||||
* The media data model used on the renderer.
|
||||
*
|
||||
* @type {goog.ui.media.MediaModel}
|
||||
* @private
|
||||
*/
|
||||
goog.ui.media.Media.prototype.dataModel_;
|
||||
|
||||
|
||||
/**
|
||||
* Sets the media model to be used on the renderer.
|
||||
* @param {goog.ui.media.MediaModel} dataModel The media model the renderer
|
||||
* should use.
|
||||
*/
|
||||
goog.ui.media.Media.prototype.setDataModel = function(dataModel) {
|
||||
this.dataModel_ = dataModel;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Gets the media model renderer is using.
|
||||
* @return {goog.ui.media.MediaModel} The media model being used.
|
||||
*/
|
||||
goog.ui.media.Media.prototype.getDataModel = function() {
|
||||
return this.dataModel_;
|
||||
};
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Base class of all media renderers. Provides the common renderer functionality
|
||||
* of medias.
|
||||
*
|
||||
* The current common functionality shared by Medias is to have an outer frame
|
||||
* that gets highlighted on mouse hover.
|
||||
*
|
||||
* TODO(user): implement more common UI behavior, as needed.
|
||||
*
|
||||
* NOTE(user): I am not enjoying how the subclasses are changing their state
|
||||
* through setState() ... maybe provide abstract methods like
|
||||
* goog.ui.media.MediaRenderer.prototype.preview = goog.abstractMethod;
|
||||
* goog.ui.media.MediaRenderer.prototype.play = goog.abstractMethod;
|
||||
* goog.ui.media.MediaRenderer.prototype.minimize = goog.abstractMethod;
|
||||
* goog.ui.media.MediaRenderer.prototype.maximize = goog.abstractMethod;
|
||||
* and call them on this parent class setState ?
|
||||
*
|
||||
* @constructor
|
||||
* @extends {goog.ui.ControlRenderer}
|
||||
*/
|
||||
goog.ui.media.MediaRenderer = function() {
|
||||
goog.ui.ControlRenderer.call(this);
|
||||
};
|
||||
goog.inherits(goog.ui.media.MediaRenderer, goog.ui.ControlRenderer);
|
||||
|
||||
|
||||
/**
|
||||
* Builds the common DOM structure of medias. Builds an outer div, and appends
|
||||
* a child div with the {@code goog.ui.Control.getContent} content. Marks the
|
||||
* caption with a {@code this.getClassClass()} + '-caption' css flag, so that
|
||||
* specific renderers can hide/show the caption as desired.
|
||||
*
|
||||
* @param {goog.ui.Control} control The control instance.
|
||||
* @return {!Element} The DOM structure that represents control.
|
||||
* @override
|
||||
*/
|
||||
goog.ui.media.MediaRenderer.prototype.createDom = function(control) {
|
||||
goog.asserts.assertInstanceof(control, goog.ui.media.Media);
|
||||
var domHelper = control.getDomHelper();
|
||||
var div = domHelper.createElement('div');
|
||||
div.className = this.getClassNames(control).join(' ');
|
||||
|
||||
var dataModel = control.getDataModel();
|
||||
|
||||
// Only creates DOMs if the data is available.
|
||||
if (dataModel.getCaption()) {
|
||||
var caption = domHelper.createElement('div');
|
||||
caption.className = goog.getCssName(this.getCssClass(), 'caption');
|
||||
caption.appendChild(domHelper.createDom(
|
||||
'p', goog.getCssName(this.getCssClass(), 'caption-text'),
|
||||
dataModel.getCaption()));
|
||||
domHelper.appendChild(div, caption);
|
||||
}
|
||||
|
||||
if (dataModel.getDescription()) {
|
||||
var description = domHelper.createElement('div');
|
||||
description.className = goog.getCssName(this.getCssClass(), 'description');
|
||||
description.appendChild(domHelper.createDom(
|
||||
'p', goog.getCssName(this.getCssClass(), 'description-text'),
|
||||
dataModel.getDescription()));
|
||||
domHelper.appendChild(div, description);
|
||||
}
|
||||
|
||||
// Creates thumbnails of the media.
|
||||
var thumbnails = dataModel.getThumbnails() || [];
|
||||
for (var index = 0; index < thumbnails.length; index++) {
|
||||
var thumbnail = thumbnails[index];
|
||||
var thumbnailElement = domHelper.createElement('img');
|
||||
thumbnailElement.src = thumbnail.getUrl();
|
||||
thumbnailElement.className = this.getThumbnailCssName(index);
|
||||
|
||||
// Check that the size is defined and that the size's height and width
|
||||
// are defined. Undefined height and width is deprecated but still
|
||||
// seems to exist in some cases.
|
||||
var size = thumbnail.getSize();
|
||||
|
||||
if (size && goog.isDefAndNotNull(size.height) &&
|
||||
goog.isDefAndNotNull(size.width)) {
|
||||
goog.style.setSize(thumbnailElement, size);
|
||||
}
|
||||
domHelper.appendChild(div, thumbnailElement);
|
||||
}
|
||||
|
||||
if (dataModel.getPlayer()) {
|
||||
// if medias have players, allow UI for a play button.
|
||||
var playButton = domHelper.createElement('div');
|
||||
playButton.className = goog.getCssName(this.getCssClass(), 'playbutton');
|
||||
domHelper.appendChild(div, playButton);
|
||||
}
|
||||
|
||||
control.setElementInternal(div);
|
||||
|
||||
this.setState(
|
||||
control,
|
||||
/** @type {goog.ui.Component.State} */ (control.getState()),
|
||||
true);
|
||||
|
||||
return div;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Returns a renamable CSS class name for a numbered thumbnail. The default
|
||||
* implementation generates the class names goog-ui-media-thumbnail0,
|
||||
* goog-ui-media-thumbnail1, and the generic goog-ui-media-thumbnailn.
|
||||
* Subclasses can override this method when their media requires additional
|
||||
* specific class names (Applications are supposed to know how many thumbnails
|
||||
* media will have).
|
||||
*
|
||||
* @param {number} index The thumbnail index.
|
||||
* @return {string} CSS class name.
|
||||
* @protected
|
||||
*/
|
||||
goog.ui.media.MediaRenderer.prototype.getThumbnailCssName = function(index) {
|
||||
switch (index) {
|
||||
case 0: return goog.getCssName(this.getCssClass(), 'thumbnail0');
|
||||
case 1: return goog.getCssName(this.getCssClass(), 'thumbnail1');
|
||||
case 2: return goog.getCssName(this.getCssClass(), 'thumbnail2');
|
||||
case 3: return goog.getCssName(this.getCssClass(), 'thumbnail3');
|
||||
case 4: return goog.getCssName(this.getCssClass(), 'thumbnail4');
|
||||
default: return goog.getCssName(this.getCssClass(), 'thumbnailn');
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,24 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<!--
|
||||
Copyright 2009 The Closure Library Authors. All Rights Reserved.
|
||||
|
||||
Use of this source code is governed by the Apache License, Version 2.0.
|
||||
See the COPYING file for details.
|
||||
-->
|
||||
<!--
|
||||
-->
|
||||
<head>
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
||||
<title>
|
||||
Closure Unit Tests - goog.ui.media.Media
|
||||
</title>
|
||||
<script src="../../base.js">
|
||||
</script>
|
||||
<script>
|
||||
goog.require('goog.ui.media.MediaTest');
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,130 @@
|
||||
// Copyright 2009 The Closure Library Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS-IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
goog.provide('goog.ui.media.MediaTest');
|
||||
goog.setTestOnly('goog.ui.media.MediaTest');
|
||||
|
||||
goog.require('goog.dom');
|
||||
goog.require('goog.math.Size');
|
||||
goog.require('goog.testing.jsunit');
|
||||
goog.require('goog.ui.ControlRenderer');
|
||||
goog.require('goog.ui.media.Media');
|
||||
goog.require('goog.ui.media.MediaModel');
|
||||
goog.require('goog.ui.media.MediaRenderer');
|
||||
var control; // The name 'media' collides with a built-in var in Chrome.
|
||||
var renderer;
|
||||
var model;
|
||||
|
||||
function setUp() {
|
||||
renderer = new goog.ui.media.MediaRenderer();
|
||||
model = new goog.ui.media.MediaModel(
|
||||
'http://url.com', 'a caption', 'a description');
|
||||
control = new goog.ui.media.Media(model, renderer);
|
||||
}
|
||||
|
||||
function tearDown() {
|
||||
control.dispose();
|
||||
}
|
||||
|
||||
function testBasicElements() {
|
||||
var model = new goog.ui.media.MediaModel(
|
||||
'http://url.com', 'a caption', 'a description');
|
||||
var thumb1 = new goog.ui.media.MediaModel.Thumbnail(
|
||||
'http://thumb.com/small.jpg', new goog.math.Size(320, 288));
|
||||
var thumb2 = new goog.ui.media.MediaModel.Thumbnail(
|
||||
'http://thumb.com/big.jpg', new goog.math.Size(800, 600));
|
||||
model.setThumbnails([thumb1, thumb2]);
|
||||
model.setPlayer(new goog.ui.media.MediaModel.Player(
|
||||
'http://media/player.swf'));
|
||||
var control = new goog.ui.media.Media(model, renderer);
|
||||
control.render();
|
||||
|
||||
var caption = goog.dom.getElementsByTagNameAndClass(
|
||||
undefined,
|
||||
goog.ui.ControlRenderer.CSS_CLASS + '-caption');
|
||||
var description = goog.dom.getElementsByTagNameAndClass(
|
||||
undefined,
|
||||
goog.ui.ControlRenderer.CSS_CLASS + '-description');
|
||||
var thumbnail0 = goog.dom.getElementsByTagNameAndClass(
|
||||
'img',
|
||||
goog.ui.ControlRenderer.CSS_CLASS + '-thumbnail0');
|
||||
var thumbnail1 = goog.dom.getElementsByTagNameAndClass(
|
||||
'img',
|
||||
goog.ui.ControlRenderer.CSS_CLASS + '-thumbnail1');
|
||||
var player = goog.dom.getElementsByTagNameAndClass(
|
||||
'iframe',
|
||||
goog.ui.ControlRenderer.CSS_CLASS + '-player');
|
||||
|
||||
assertNotNull(caption);
|
||||
assertEquals(1, caption.length);
|
||||
assertNotNull(description);
|
||||
assertEquals(1, description.length);
|
||||
assertNotNull(thumbnail0);
|
||||
assertEquals(1, thumbnail0.length);
|
||||
assertEquals('320px', thumbnail0[0].style.width);
|
||||
assertEquals('288px', thumbnail0[0].style.height);
|
||||
assertEquals('http://thumb.com/small.jpg', thumbnail0[0].src);
|
||||
assertNotNull(thumbnail1);
|
||||
assertEquals(1, thumbnail1.length);
|
||||
assertEquals('800px', thumbnail1[0].style.width);
|
||||
assertEquals('600px', thumbnail1[0].style.height);
|
||||
assertEquals('http://thumb.com/big.jpg', thumbnail1[0].src);
|
||||
// players are only shown when media is selected
|
||||
assertNotNull(player);
|
||||
assertEquals(0, player.length);
|
||||
|
||||
control.dispose();
|
||||
}
|
||||
|
||||
function testDoesntCreatesCaptionIfUnavailable() {
|
||||
var incompleteModel = new goog.ui.media.MediaModel(
|
||||
'http://url.com', undefined, 'a description');
|
||||
incompleteMedia = new goog.ui.media.Media('', renderer);
|
||||
incompleteMedia.setDataModel(incompleteModel);
|
||||
incompleteMedia.render();
|
||||
var caption = goog.dom.getElementsByTagNameAndClass(
|
||||
undefined,
|
||||
goog.ui.ControlRenderer.CSS_CLASS + '-caption');
|
||||
var description = goog.dom.getElementsByTagNameAndClass(
|
||||
undefined,
|
||||
goog.ui.ControlRenderer.CSS_CLASS + '-description');
|
||||
assertEquals(0, caption.length);
|
||||
assertNotNull(description);
|
||||
incompleteMedia.dispose();
|
||||
}
|
||||
|
||||
function testSetAriaLabel() {
|
||||
var model = new goog.ui.media.MediaModel(
|
||||
'http://url.com', 'a caption', 'a description');
|
||||
var thumb1 = new goog.ui.media.MediaModel.Thumbnail(
|
||||
'http://thumb.com/small.jpg', new goog.math.Size(320, 288));
|
||||
var thumb2 = new goog.ui.media.MediaModel.Thumbnail(
|
||||
'http://thumb.com/big.jpg', new goog.math.Size(800, 600));
|
||||
model.setThumbnails([thumb1, thumb2]);
|
||||
model.setPlayer(new goog.ui.media.MediaModel.Player(
|
||||
'http://media/player.swf'));
|
||||
var control = new goog.ui.media.Media(model, renderer);
|
||||
assertNull('Media must not have aria label by default',
|
||||
control.getAriaLabel());
|
||||
control.setAriaLabel('My media');
|
||||
control.render();
|
||||
var element = control.getElementStrict();
|
||||
assertNotNull('Element must not be null', element);
|
||||
assertEquals('Media element must have expected aria-label', 'My media',
|
||||
element.getAttribute('aria-label'));
|
||||
assertTrue(goog.dom.isFocusableTabIndex(element));
|
||||
control.setAriaLabel('My new media');
|
||||
assertEquals('Media element must have updated aria-label', 'My new media',
|
||||
element.getAttribute('aria-label'));
|
||||
}
|
||||
@@ -0,0 +1,978 @@
|
||||
// Copyright 2009 The Closure Library Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS-IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
/**
|
||||
* @fileoverview Provides the base media model consistent with the Yahoo Media
|
||||
* RSS specification {@link http://search.yahoo.com/mrss/}.
|
||||
*/
|
||||
|
||||
goog.provide('goog.ui.media.MediaModel');
|
||||
goog.provide('goog.ui.media.MediaModel.Category');
|
||||
goog.provide('goog.ui.media.MediaModel.Credit');
|
||||
goog.provide('goog.ui.media.MediaModel.Credit.Role');
|
||||
goog.provide('goog.ui.media.MediaModel.Credit.Scheme');
|
||||
goog.provide('goog.ui.media.MediaModel.Medium');
|
||||
goog.provide('goog.ui.media.MediaModel.MimeType');
|
||||
goog.provide('goog.ui.media.MediaModel.Player');
|
||||
goog.provide('goog.ui.media.MediaModel.SubTitle');
|
||||
goog.provide('goog.ui.media.MediaModel.Thumbnail');
|
||||
|
||||
goog.require('goog.array');
|
||||
goog.require('goog.html.TrustedResourceUrl');
|
||||
goog.require('goog.html.legacyconversions');
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* An base data value class for all media data models.
|
||||
*
|
||||
* MediaModels are exact matches to the fields defined in the Yahoo RSS media
|
||||
* specification {@link http://search.yahoo.com/mrss/}.
|
||||
*
|
||||
* The current common data shared by medias is to have URLs, mime types,
|
||||
* captions, descriptions, thumbnails and players. Some of these may not be
|
||||
* available, or applications may not want to render them, so {@code null}
|
||||
* values are allowed. {@code goog.ui.media.MediaRenderer} checks whether the
|
||||
* values are available before creating DOMs for them.
|
||||
*
|
||||
* TODO(user): support asynchronous data models by subclassing
|
||||
* {@link goog.events.EventTarget} or {@link goog.ds.DataNode}. Understand why
|
||||
* {@link http://goto/datanode} is not available in closure. Add setters to
|
||||
* MediaModel once this is supported.
|
||||
*
|
||||
* @param {string=} opt_url An optional URL of the media.
|
||||
* @param {string=} opt_caption An optional caption of the media.
|
||||
* @param {string=} opt_description An optional description of the media.
|
||||
* @param {goog.ui.media.MediaModel.MimeType=} opt_type The type of the media.
|
||||
* @param {goog.ui.media.MediaModel.Medium=} opt_medium The medium of the media.
|
||||
* @param {number=} opt_duration The duration of the media in seconds.
|
||||
* @param {number=} opt_width The width of the media in pixels.
|
||||
* @param {number=} opt_height The height of the media in pixels.
|
||||
* @constructor
|
||||
*/
|
||||
goog.ui.media.MediaModel = function(opt_url,
|
||||
opt_caption,
|
||||
opt_description,
|
||||
opt_type,
|
||||
opt_medium,
|
||||
opt_duration,
|
||||
opt_width,
|
||||
opt_height) {
|
||||
/**
|
||||
* The URL of the media.
|
||||
* @type {string|undefined}
|
||||
* @private
|
||||
*/
|
||||
this.url_ = opt_url;
|
||||
|
||||
/**
|
||||
* The caption of the media.
|
||||
* @type {string|undefined}
|
||||
* @private
|
||||
*/
|
||||
this.caption_ = opt_caption;
|
||||
|
||||
/**
|
||||
* A description of the media, typically user generated comments about it.
|
||||
* @type {string|undefined}
|
||||
* @private
|
||||
*/
|
||||
this.description_ = opt_description;
|
||||
|
||||
/**
|
||||
* The mime type of the media.
|
||||
* @type {goog.ui.media.MediaModel.MimeType|undefined}
|
||||
* @private
|
||||
*/
|
||||
this.type_ = opt_type;
|
||||
|
||||
/**
|
||||
* The medium of the media.
|
||||
* @type {goog.ui.media.MediaModel.Medium|undefined}
|
||||
* @private
|
||||
*/
|
||||
this.medium_ = opt_medium;
|
||||
|
||||
/**
|
||||
* The duration of the media in seconds.
|
||||
* @type {number|undefined}
|
||||
* @private
|
||||
*/
|
||||
this.duration_ = opt_duration;
|
||||
|
||||
/**
|
||||
* The width of the media in pixels.
|
||||
* @type {number|undefined}
|
||||
* @private
|
||||
*/
|
||||
this.width_ = opt_width;
|
||||
|
||||
/**
|
||||
* The height of the media in pixels.
|
||||
* @type {number|undefined}
|
||||
* @private
|
||||
*/
|
||||
this.height_ = opt_height;
|
||||
|
||||
/**
|
||||
* A list of thumbnails representations of the media (eg different sizes of
|
||||
* the same photo, etc).
|
||||
* @type {Array<goog.ui.media.MediaModel.Thumbnail>}
|
||||
* @private
|
||||
*/
|
||||
this.thumbnails_ = [];
|
||||
|
||||
/**
|
||||
* The list of categories that are applied to this media.
|
||||
* @type {Array<goog.ui.media.MediaModel.Category>}
|
||||
* @private
|
||||
*/
|
||||
this.categories_ = [];
|
||||
|
||||
/**
|
||||
* The list of credits that pertain to this media object.
|
||||
* @type {!Array<goog.ui.media.MediaModel.Credit>}
|
||||
* @private
|
||||
*/
|
||||
this.credits_ = [];
|
||||
|
||||
/**
|
||||
* The list of subtitles for the media object.
|
||||
* @type {Array<goog.ui.media.MediaModel.SubTitle>}
|
||||
* @private
|
||||
*/
|
||||
this.subTitles_ = [];
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* The supported media mime types, a subset of the media types found here:
|
||||
* {@link http://www.iana.org/assignments/media-types/} and here
|
||||
* {@link http://en.wikipedia.org/wiki/Internet_media_type}
|
||||
* @enum {string}
|
||||
*/
|
||||
goog.ui.media.MediaModel.MimeType = {
|
||||
HTML: 'text/html',
|
||||
PLAIN: 'text/plain',
|
||||
FLASH: 'application/x-shockwave-flash',
|
||||
JPEG: 'image/jpeg',
|
||||
GIF: 'image/gif',
|
||||
PNG: 'image/png'
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Supported mediums, found here:
|
||||
* {@link http://video.search.yahoo.com/mrss}
|
||||
* @enum {string}
|
||||
*/
|
||||
goog.ui.media.MediaModel.Medium = {
|
||||
IMAGE: 'image',
|
||||
AUDIO: 'audio',
|
||||
VIDEO: 'video',
|
||||
DOCUMENT: 'document',
|
||||
EXECUTABLE: 'executable'
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* The media player.
|
||||
* @type {goog.ui.media.MediaModel.Player}
|
||||
* @private
|
||||
*/
|
||||
goog.ui.media.MediaModel.prototype.player_;
|
||||
|
||||
|
||||
/**
|
||||
* Gets the URL of this media.
|
||||
* @return {string|undefined} The URL of the media.
|
||||
*/
|
||||
goog.ui.media.MediaModel.prototype.getUrl = function() {
|
||||
return this.url_;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Sets the URL of this media.
|
||||
* @param {string} url The URL of the media.
|
||||
* @return {!goog.ui.media.MediaModel} The object itself, used for chaining.
|
||||
*/
|
||||
goog.ui.media.MediaModel.prototype.setUrl = function(url) {
|
||||
this.url_ = url;
|
||||
return this;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Gets the caption of this media.
|
||||
* @return {string|undefined} The caption of the media.
|
||||
*/
|
||||
goog.ui.media.MediaModel.prototype.getCaption = function() {
|
||||
return this.caption_;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Sets the caption of this media.
|
||||
* @param {string} caption The caption of the media.
|
||||
* @return {!goog.ui.media.MediaModel} The object itself, used for chaining.
|
||||
*/
|
||||
goog.ui.media.MediaModel.prototype.setCaption = function(caption) {
|
||||
this.caption_ = caption;
|
||||
return this;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Gets the media mime type.
|
||||
* @return {goog.ui.media.MediaModel.MimeType|undefined} The media mime type.
|
||||
*/
|
||||
goog.ui.media.MediaModel.prototype.getType = function() {
|
||||
return this.type_;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Sets the media mime type.
|
||||
* @param {goog.ui.media.MediaModel.MimeType} type The media mime type.
|
||||
* @return {!goog.ui.media.MediaModel} The object itself, used for chaining.
|
||||
*/
|
||||
goog.ui.media.MediaModel.prototype.setType = function(type) {
|
||||
this.type_ = type;
|
||||
return this;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Gets the media medium.
|
||||
* @return {goog.ui.media.MediaModel.Medium|undefined} The media medium.
|
||||
*/
|
||||
goog.ui.media.MediaModel.prototype.getMedium = function() {
|
||||
return this.medium_;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Sets the media medium.
|
||||
* @param {goog.ui.media.MediaModel.Medium} medium The media medium.
|
||||
* @return {!goog.ui.media.MediaModel} The object itself, used for chaining.
|
||||
*/
|
||||
goog.ui.media.MediaModel.prototype.setMedium = function(medium) {
|
||||
this.medium_ = medium;
|
||||
return this;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Gets the description of this media.
|
||||
* @return {string|undefined} The description of the media.
|
||||
*/
|
||||
goog.ui.media.MediaModel.prototype.getDescription = function() {
|
||||
return this.description_;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Sets the description of this media.
|
||||
* @param {string} description The description of the media.
|
||||
* @return {!goog.ui.media.MediaModel} The object itself, used for chaining.
|
||||
*/
|
||||
goog.ui.media.MediaModel.prototype.setDescription = function(description) {
|
||||
this.description_ = description;
|
||||
return this;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Gets the thumbnail urls.
|
||||
* @return {Array<goog.ui.media.MediaModel.Thumbnail>} The list of thumbnails.
|
||||
*/
|
||||
goog.ui.media.MediaModel.prototype.getThumbnails = function() {
|
||||
return this.thumbnails_;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Sets the thumbnail list.
|
||||
* @param {Array<goog.ui.media.MediaModel.Thumbnail>} thumbnails The list of
|
||||
* thumbnail.
|
||||
* @return {!goog.ui.media.MediaModel} The object itself, used for chaining.
|
||||
*/
|
||||
goog.ui.media.MediaModel.prototype.setThumbnails = function(thumbnails) {
|
||||
this.thumbnails_ = thumbnails;
|
||||
return this;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Gets the duration of the media.
|
||||
* @return {number|undefined} The duration in seconds.
|
||||
*/
|
||||
goog.ui.media.MediaModel.prototype.getDuration = function() {
|
||||
return this.duration_;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Sets duration of the media.
|
||||
* @param {number} duration The duration of the media, in seconds.
|
||||
* @return {!goog.ui.media.MediaModel} The object itself, used for chaining.
|
||||
*/
|
||||
goog.ui.media.MediaModel.prototype.setDuration = function(duration) {
|
||||
this.duration_ = duration;
|
||||
return this;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Gets the width of the media in pixels.
|
||||
* @return {number|undefined} The width in pixels.
|
||||
*/
|
||||
goog.ui.media.MediaModel.prototype.getWidth = function() {
|
||||
return this.width_;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Sets the width of the media.
|
||||
* @param {number} width The width of the media, in pixels.
|
||||
* @return {!goog.ui.media.MediaModel} The object itself, used for chaining.
|
||||
*/
|
||||
goog.ui.media.MediaModel.prototype.setWidth = function(width) {
|
||||
this.width_ = width;
|
||||
return this;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Gets the height of the media in pixels.
|
||||
* @return {number|undefined} The height in pixels.
|
||||
*/
|
||||
goog.ui.media.MediaModel.prototype.getHeight = function() {
|
||||
return this.height_;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Sets the height of the media.
|
||||
* @param {number} height The height of the media, in pixels.
|
||||
* @return {!goog.ui.media.MediaModel} The object itself, used for chaining.
|
||||
*/
|
||||
goog.ui.media.MediaModel.prototype.setHeight = function(height) {
|
||||
this.height_ = height;
|
||||
return this;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Gets the player data.
|
||||
* @return {goog.ui.media.MediaModel.Player|undefined} The media player data.
|
||||
*/
|
||||
goog.ui.media.MediaModel.prototype.getPlayer = function() {
|
||||
return this.player_;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Sets the player data.
|
||||
* @param {goog.ui.media.MediaModel.Player} player The media player data.
|
||||
* @return {!goog.ui.media.MediaModel} The object itself, used for chaining.
|
||||
*/
|
||||
goog.ui.media.MediaModel.prototype.setPlayer = function(player) {
|
||||
this.player_ = player;
|
||||
return this;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Gets the categories of the media.
|
||||
* @return {Array<goog.ui.media.MediaModel.Category>} The categories of the
|
||||
* media.
|
||||
*/
|
||||
goog.ui.media.MediaModel.prototype.getCategories = function() {
|
||||
return this.categories_;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Sets the categories of the media
|
||||
* @param {Array<goog.ui.media.MediaModel.Category>} categories The categories
|
||||
* of the media.
|
||||
* @return {!goog.ui.media.MediaModel} The object itself, used for chaining.
|
||||
*/
|
||||
goog.ui.media.MediaModel.prototype.setCategories = function(categories) {
|
||||
this.categories_ = categories;
|
||||
return this;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Finds the first category with the given scheme.
|
||||
* @param {string} scheme The scheme to search for.
|
||||
* @return {goog.ui.media.MediaModel.Category} The category that has the
|
||||
* given scheme. May be null.
|
||||
*/
|
||||
goog.ui.media.MediaModel.prototype.findCategoryWithScheme = function(scheme) {
|
||||
if (!this.categories_) {
|
||||
return null;
|
||||
}
|
||||
var category = goog.array.find(this.categories_, function(category) {
|
||||
return category ? (scheme == category.getScheme()) : false;
|
||||
});
|
||||
return /** @type {goog.ui.media.MediaModel.Category} */ (category);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Gets the credits of the media.
|
||||
* @return {!Array<goog.ui.media.MediaModel.Credit>} The credits of the media.
|
||||
*/
|
||||
goog.ui.media.MediaModel.prototype.getCredits = function() {
|
||||
return this.credits_;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Sets the credits of the media
|
||||
* @param {!Array<goog.ui.media.MediaModel.Credit>} credits The credits of the
|
||||
* media.
|
||||
* @return {!goog.ui.media.MediaModel} The object itself, used for chaining.
|
||||
*/
|
||||
goog.ui.media.MediaModel.prototype.setCredits = function(credits) {
|
||||
this.credits_ = credits;
|
||||
return this;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Finds all credits with the given role.
|
||||
* @param {string} role The role to search for.
|
||||
* @return {!Array<!goog.ui.media.MediaModel.Credit>} An array of credits
|
||||
* with the given role. May be empty.
|
||||
*/
|
||||
goog.ui.media.MediaModel.prototype.findCreditsWithRole = function(role) {
|
||||
var credits = goog.array.filter(this.credits_, function(credit) {
|
||||
return role == credit.getRole();
|
||||
});
|
||||
return /** @type {!Array<!goog.ui.media.MediaModel.Credit>} */ (credits);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Gets the subtitles for the media.
|
||||
* @return {Array<goog.ui.media.MediaModel.SubTitle>} The subtitles.
|
||||
*/
|
||||
goog.ui.media.MediaModel.prototype.getSubTitles = function() {
|
||||
return this.subTitles_;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Sets the subtitles for the media
|
||||
* @param {Array<goog.ui.media.MediaModel.SubTitle>} subtitles The subtitles.
|
||||
* @return {!goog.ui.media.MediaModel} The object itself.
|
||||
*/
|
||||
goog.ui.media.MediaModel.prototype.setSubTitles = function(subtitles) {
|
||||
this.subTitles_ = subtitles;
|
||||
return this;
|
||||
};
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Constructs a thumbnail containing details of the thumbnail's image URL and
|
||||
* optionally its size.
|
||||
* @param {string} url The URL of the thumbnail's image.
|
||||
* @param {goog.math.Size=} opt_size The size of the thumbnail's image if known.
|
||||
* @constructor
|
||||
* @final
|
||||
*/
|
||||
goog.ui.media.MediaModel.Thumbnail = function(url, opt_size) {
|
||||
/**
|
||||
* The thumbnail's image URL.
|
||||
* @type {string}
|
||||
* @private
|
||||
*/
|
||||
this.url_ = url;
|
||||
|
||||
/**
|
||||
* The size of the thumbnail's image if known.
|
||||
* @type {goog.math.Size}
|
||||
* @private
|
||||
*/
|
||||
this.size_ = opt_size || null;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Gets the thumbnail URL.
|
||||
* @return {string} The thumbnail's image URL.
|
||||
*/
|
||||
goog.ui.media.MediaModel.Thumbnail.prototype.getUrl = function() {
|
||||
return this.url_;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Sets the thumbnail URL.
|
||||
* @param {string} url The thumbnail's image URL.
|
||||
* @return {!goog.ui.media.MediaModel.Thumbnail} The object itself, used for
|
||||
* chaining.
|
||||
*/
|
||||
goog.ui.media.MediaModel.Thumbnail.prototype.setUrl = function(url) {
|
||||
this.url_ = url;
|
||||
return this;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Gets the thumbnail size.
|
||||
* @return {goog.math.Size} The size of the thumbnail's image if known.
|
||||
*/
|
||||
goog.ui.media.MediaModel.Thumbnail.prototype.getSize = function() {
|
||||
return this.size_;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Sets the thumbnail size.
|
||||
* @param {goog.math.Size} size The size of the thumbnail's image.
|
||||
* @return {!goog.ui.media.MediaModel.Thumbnail} The object itself, used for
|
||||
* chaining.
|
||||
*/
|
||||
goog.ui.media.MediaModel.Thumbnail.prototype.setSize = function(size) {
|
||||
this.size_ = size;
|
||||
return this;
|
||||
};
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Constructs a player containing details of the player's URL and
|
||||
* optionally its size.
|
||||
* @param {string|!goog.html.TrustedResourceUrl} url The URL of the player.
|
||||
* @param {Object=} opt_vars Optional map of arguments to the player.
|
||||
* @param {goog.math.Size=} opt_size The size of the player if known.
|
||||
* @constructor
|
||||
* @final
|
||||
*/
|
||||
goog.ui.media.MediaModel.Player = function(url, opt_vars, opt_size) {
|
||||
/**
|
||||
* The player's URL.
|
||||
* @type {!goog.html.TrustedResourceUrl}
|
||||
* @private
|
||||
*/
|
||||
this.trustedResourceUrl_ = url instanceof goog.html.TrustedResourceUrl ? url :
|
||||
goog.html.legacyconversions.trustedResourceUrlFromString(url);
|
||||
|
||||
/**
|
||||
* Player arguments, typically flash arguments.
|
||||
* @type {Object}
|
||||
* @private
|
||||
*/
|
||||
this.vars_ = opt_vars || null;
|
||||
|
||||
/**
|
||||
* The size of the player if known.
|
||||
* @type {goog.math.Size}
|
||||
* @private
|
||||
*/
|
||||
this.size_ = opt_size || null;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Gets the player URL.
|
||||
* @return {!goog.html.TrustedResourceUrl} The player's URL.
|
||||
*/
|
||||
goog.ui.media.MediaModel.Player.prototype.getTrustedResourceUrl = function() {
|
||||
return this.trustedResourceUrl_;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Gets the player URL.
|
||||
* @return {string} The player's URL.
|
||||
*/
|
||||
goog.ui.media.MediaModel.Player.prototype.getUrl = function() {
|
||||
return this.trustedResourceUrl_.getTypedStringValue();
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Sets the player URL.
|
||||
* @param {string|!goog.html.TrustedResourceUrl} url The player's URL.
|
||||
* @return {!goog.ui.media.MediaModel.Player} The object itself, used for
|
||||
* chaining.
|
||||
*/
|
||||
goog.ui.media.MediaModel.Player.prototype.setUrl = function(url) {
|
||||
this.trustedResourceUrl_ = url instanceof goog.html.TrustedResourceUrl ? url :
|
||||
goog.html.legacyconversions.trustedResourceUrlFromString(url);
|
||||
return this;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Gets the player arguments.
|
||||
* @return {Object} The media player arguments.
|
||||
*/
|
||||
goog.ui.media.MediaModel.Player.prototype.getVars = function() {
|
||||
return this.vars_;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Sets the player arguments.
|
||||
* @param {Object} vars The media player arguments.
|
||||
* @return {!goog.ui.media.MediaModel.Player} The object itself, used for
|
||||
* chaining.
|
||||
*/
|
||||
goog.ui.media.MediaModel.Player.prototype.setVars = function(vars) {
|
||||
this.vars_ = vars;
|
||||
return this;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Gets the size of the player.
|
||||
* @return {goog.math.Size} The size of the player if known.
|
||||
*/
|
||||
goog.ui.media.MediaModel.Player.prototype.getSize = function() {
|
||||
return this.size_;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Sets the size of the player.
|
||||
* @param {goog.math.Size} size The size of the player.
|
||||
* @return {!goog.ui.media.MediaModel.Player} The object itself, used for
|
||||
* chaining.
|
||||
*/
|
||||
goog.ui.media.MediaModel.Player.prototype.setSize = function(size) {
|
||||
this.size_ = size;
|
||||
return this;
|
||||
};
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* A taxonomy to be set that gives an indication of the type of media content,
|
||||
* and its particular contents.
|
||||
* @param {string} scheme The URI that identifies the categorization scheme.
|
||||
* @param {string} value The value of the category.
|
||||
* @param {string=} opt_label The human readable label that can be displayed in
|
||||
* end user applications.
|
||||
* @constructor
|
||||
* @final
|
||||
*/
|
||||
goog.ui.media.MediaModel.Category = function(scheme, value, opt_label) {
|
||||
/**
|
||||
* The URI that identifies the categorization scheme.
|
||||
* @type {string}
|
||||
* @private
|
||||
*/
|
||||
this.scheme_ = scheme;
|
||||
|
||||
/**
|
||||
* The value of the category.
|
||||
* @type {string}
|
||||
* @private
|
||||
*/
|
||||
this.value_ = value;
|
||||
|
||||
/**
|
||||
* The human readable label that can be displayed in end user applications.
|
||||
* @type {string}
|
||||
* @private
|
||||
*/
|
||||
this.label_ = opt_label || '';
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Gets the category scheme.
|
||||
* @return {string} The category scheme URI.
|
||||
*/
|
||||
goog.ui.media.MediaModel.Category.prototype.getScheme = function() {
|
||||
return this.scheme_;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Sets the category scheme.
|
||||
* @param {string} scheme The category's scheme.
|
||||
* @return {!goog.ui.media.MediaModel.Category} The object itself, used for
|
||||
* chaining.
|
||||
*/
|
||||
goog.ui.media.MediaModel.Category.prototype.setScheme = function(scheme) {
|
||||
this.scheme_ = scheme;
|
||||
return this;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Gets the categor's value.
|
||||
* @return {string} The category's value.
|
||||
*/
|
||||
goog.ui.media.MediaModel.Category.prototype.getValue = function() {
|
||||
return this.value_;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Sets the category value.
|
||||
* @param {string} value The category value to be set.
|
||||
* @return {!goog.ui.media.MediaModel.Category} The object itself, used for
|
||||
* chaining.
|
||||
*/
|
||||
goog.ui.media.MediaModel.Category.prototype.setValue = function(value) {
|
||||
this.value_ = value;
|
||||
return this;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Gets the label of the category.
|
||||
* @return {string} The label of the category.
|
||||
*/
|
||||
goog.ui.media.MediaModel.Category.prototype.getLabel = function() {
|
||||
return this.label_;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Sets the label of the category.
|
||||
* @param {string} label The label of the category.
|
||||
* @return {!goog.ui.media.MediaModel.Category} The object itself, used for
|
||||
* chaining.
|
||||
*/
|
||||
goog.ui.media.MediaModel.Category.prototype.setLabel = function(label) {
|
||||
this.label_ = label;
|
||||
return this;
|
||||
};
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Indicates an entity that has contributed to a media object. Based on
|
||||
* 'media.credit' in the rss spec.
|
||||
* @param {string} value The name of the entity being credited.
|
||||
* @param {goog.ui.media.MediaModel.Credit.Role=} opt_role The role the entity
|
||||
* played.
|
||||
* @param {goog.ui.media.MediaModel.Credit.Scheme=} opt_scheme The URI that
|
||||
* identifies the role scheme.
|
||||
* @constructor
|
||||
* @final
|
||||
*/
|
||||
goog.ui.media.MediaModel.Credit = function(value, opt_role, opt_scheme) {
|
||||
/**
|
||||
* The name of entity being credited.
|
||||
* @type {string}
|
||||
* @private
|
||||
*/
|
||||
this.value_ = value;
|
||||
|
||||
/**
|
||||
* The role the entity played.
|
||||
* @type {goog.ui.media.MediaModel.Credit.Role|undefined}
|
||||
* @private
|
||||
*/
|
||||
this.role_ = opt_role;
|
||||
|
||||
/**
|
||||
* The URI that identifies the role scheme
|
||||
* @type {goog.ui.media.MediaModel.Credit.Scheme|undefined}
|
||||
* @private
|
||||
*/
|
||||
this.scheme_ = opt_scheme;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* The types of known roles.
|
||||
* @enum {string}
|
||||
*/
|
||||
goog.ui.media.MediaModel.Credit.Role = {
|
||||
UPLOADER: 'uploader',
|
||||
OWNER: 'owner'
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* The types of known schemes.
|
||||
* @enum {string}
|
||||
*/
|
||||
goog.ui.media.MediaModel.Credit.Scheme = {
|
||||
EUROPEAN_BROADCASTING: 'urn:ebu',
|
||||
YAHOO: 'urn:yvs',
|
||||
YOUTUBE: 'urn:youtube'
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Gets the name of the entity being credited.
|
||||
* @return {string} The name of the entity.
|
||||
*/
|
||||
goog.ui.media.MediaModel.Credit.prototype.getValue = function() {
|
||||
return this.value_;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Sets the value of the credit object.
|
||||
* @param {string} value The value.
|
||||
* @return {!goog.ui.media.MediaModel.Credit} The object itself.
|
||||
*/
|
||||
goog.ui.media.MediaModel.Credit.prototype.setValue = function(value) {
|
||||
this.value_ = value;
|
||||
return this;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Gets the role of the entity being credited.
|
||||
* @return {goog.ui.media.MediaModel.Credit.Role|undefined} The role of the
|
||||
* entity.
|
||||
*/
|
||||
goog.ui.media.MediaModel.Credit.prototype.getRole = function() {
|
||||
return this.role_;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Sets the role of the credit object.
|
||||
* @param {goog.ui.media.MediaModel.Credit.Role} role The role.
|
||||
* @return {!goog.ui.media.MediaModel.Credit} The object itself.
|
||||
*/
|
||||
goog.ui.media.MediaModel.Credit.prototype.setRole = function(role) {
|
||||
this.role_ = role;
|
||||
return this;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Gets the scheme of the credit object.
|
||||
* @return {goog.ui.media.MediaModel.Credit.Scheme|undefined} The URI that
|
||||
* identifies the role scheme.
|
||||
*/
|
||||
goog.ui.media.MediaModel.Credit.prototype.getScheme = function() {
|
||||
return this.scheme_;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Sets the scheme of the credit object.
|
||||
* @param {goog.ui.media.MediaModel.Credit.Scheme} scheme The scheme.
|
||||
* @return {!goog.ui.media.MediaModel.Credit} The object itself.
|
||||
*/
|
||||
goog.ui.media.MediaModel.Credit.prototype.setScheme = function(scheme) {
|
||||
this.scheme_ = scheme;
|
||||
return this;
|
||||
};
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* A reference to the subtitle URI for a media object.
|
||||
* Implements the 'media.subTitle' in the rss spec.
|
||||
*
|
||||
* @param {string} href The subtitle's URI.
|
||||
* to fetch the subtitle file.
|
||||
* @param {string} lang An RFC 3066 language.
|
||||
* @param {string} type The MIME type of the URI.
|
||||
* @constructor
|
||||
* @final
|
||||
*/
|
||||
goog.ui.media.MediaModel.SubTitle = function(href, lang, type) {
|
||||
/**
|
||||
* The subtitle href.
|
||||
* @type {string}
|
||||
* @private
|
||||
*/
|
||||
this.href_ = href;
|
||||
|
||||
/**
|
||||
* The RFC 3066 language.
|
||||
* @type {string}
|
||||
* @private
|
||||
*/
|
||||
this.lang_ = lang;
|
||||
|
||||
/**
|
||||
* The MIME type of the resource.
|
||||
* @type {string}
|
||||
* @private
|
||||
*/
|
||||
this.type_ = type;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Sets the href for the subtitle object.
|
||||
* @param {string} href The subtitle's URI.
|
||||
* @return {!goog.ui.media.MediaModel.SubTitle} The object itself.
|
||||
*/
|
||||
goog.ui.media.MediaModel.SubTitle.prototype.setHref = function(href) {
|
||||
this.href_ = href;
|
||||
return this;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Get the href for the subtitle object.
|
||||
* @return {string} href The subtitle's URI.
|
||||
*/
|
||||
goog.ui.media.MediaModel.SubTitle.prototype.getHref = function() {
|
||||
return this.href_;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Sets the language for the subtitle object.
|
||||
* @param {string} lang The RFC 3066 language.
|
||||
* @return {!goog.ui.media.MediaModel.SubTitle} The object itself.
|
||||
*/
|
||||
goog.ui.media.MediaModel.SubTitle.prototype.setLang = function(lang) {
|
||||
this.lang_ = lang;
|
||||
return this;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Get the lang for the subtitle object.
|
||||
* @return {string} lang The RFC 3066 language.
|
||||
*/
|
||||
goog.ui.media.MediaModel.SubTitle.prototype.getLang = function() {
|
||||
return this.lang_;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Sets the type for the subtitle object.
|
||||
* @param {string} type The MIME type.
|
||||
* @return {!goog.ui.media.MediaModel.SubTitle} The object itself.
|
||||
*/
|
||||
goog.ui.media.MediaModel.SubTitle.prototype.setType = function(type) {
|
||||
this.type_ = type;
|
||||
return this;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Get the type for the subtitle object.
|
||||
* @return {string} type The MIME type.
|
||||
*/
|
||||
goog.ui.media.MediaModel.SubTitle.prototype.getType = function() {
|
||||
return this.type_;
|
||||
};
|
||||
@@ -0,0 +1,25 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<!--
|
||||
Copyright 2011 The Closure Library Authors. All Rights Reserved.
|
||||
|
||||
Use of this source code is governed by the Apache License, Version 2.0.
|
||||
See the COPYING file for details.
|
||||
-->
|
||||
<!--
|
||||
deboer@google.com (James deBoer)
|
||||
-->
|
||||
<head>
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
||||
<title>
|
||||
Closure Unit Tests - goog.ui.media.MediaModel
|
||||
</title>
|
||||
<script src="../../base.js">
|
||||
</script>
|
||||
<script>
|
||||
goog.require('goog.ui.media.MediaModelTest');
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,95 @@
|
||||
// Copyright 2011 The Closure Library Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS-IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
goog.provide('goog.ui.media.MediaModelTest');
|
||||
goog.setTestOnly('goog.ui.media.MediaModelTest');
|
||||
|
||||
goog.require('goog.testing.jsunit');
|
||||
goog.require('goog.ui.media.MediaModel');
|
||||
|
||||
|
||||
/**
|
||||
* A simple model used in many tests.
|
||||
*/
|
||||
var model;
|
||||
|
||||
function setUp() {
|
||||
model = new goog.ui.media.MediaModel(
|
||||
'http://url.com', 'a caption', 'a description');
|
||||
}
|
||||
|
||||
function testMediaModel() {
|
||||
assertEquals('http://url.com', model.getUrl());
|
||||
assertEquals('a caption', model.getCaption());
|
||||
assertEquals('a description', model.getDescription());
|
||||
|
||||
var incompleteModel = new goog.ui.media.MediaModel(
|
||||
'http://foo.bar',
|
||||
undefined,
|
||||
'This media has no caption but has a description and a URL');
|
||||
assertEquals('http://foo.bar', incompleteModel.getUrl());
|
||||
assertUndefined(incompleteModel.getCaption());
|
||||
assertEquals('This media has no caption but has a description and a URL',
|
||||
incompleteModel.getDescription());
|
||||
assertArrayEquals([], incompleteModel.getThumbnails());
|
||||
}
|
||||
|
||||
function testMediaModelFindCategoryWithScheme() {
|
||||
assertNull(model.findCategoryWithScheme('no such scheme'));
|
||||
|
||||
model.setCategories([
|
||||
new goog.ui.media.MediaModel.Category('scheme-a', 'value-a'),
|
||||
new goog.ui.media.MediaModel.Category('scheme-b', 'value-b')
|
||||
]);
|
||||
assertNull(model.findCategoryWithScheme('no such scheme'));
|
||||
assertEquals('value-a',
|
||||
model.findCategoryWithScheme('scheme-a').getValue());
|
||||
assertEquals('value-b',
|
||||
model.findCategoryWithScheme('scheme-b').getValue());
|
||||
}
|
||||
|
||||
|
||||
function testMediaModelFindCreditsWithRole() {
|
||||
assertEquals(0, model.findCreditsWithRole('no such role').length);
|
||||
|
||||
model.setCredits([
|
||||
new goog.ui.media.MediaModel.Credit('value-a', 'role-a'),
|
||||
new goog.ui.media.MediaModel.Credit('value-a2', 'role-a'),
|
||||
new goog.ui.media.MediaModel.Credit('value-b', 'role-b')
|
||||
]);
|
||||
|
||||
assertEquals(0, model.findCreditsWithRole('no such role').length);
|
||||
assertEquals(2, model.findCreditsWithRole('role-a').length);
|
||||
assertEquals('value-a',
|
||||
model.findCreditsWithRole('role-a')[0].getValue());
|
||||
assertEquals('value-a2',
|
||||
model.findCreditsWithRole('role-a')[1].getValue());
|
||||
assertEquals('value-b',
|
||||
model.findCreditsWithRole('role-b')[0].getValue());
|
||||
}
|
||||
|
||||
function testMediaModelSubtitles() {
|
||||
model.setSubTitles([
|
||||
new goog.ui.media.MediaModel.SubTitle(
|
||||
'uri', '*', 'application/tts+xml')
|
||||
]);
|
||||
assertEquals(1, model.getSubTitles().length);
|
||||
assertEquals('uri', model.getSubTitles()[0].getHref());
|
||||
assertEquals('*', model.getSubTitles()[0].getLang());
|
||||
assertEquals('application/tts+xml', model.getSubTitles()[0].getType());
|
||||
}
|
||||
|
||||
function testMediaModelNoSubtitles() {
|
||||
assertEquals(0, model.getSubTitles().length);
|
||||
}
|
||||
@@ -0,0 +1,226 @@
|
||||
// Copyright 2009 The Closure Library Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS-IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
/**
|
||||
* @fileoverview provides a reusable mp3 UI component given a mp3 URL.
|
||||
*
|
||||
* goog.ui.media.Mp3 is actually a {@link goog.ui.ControlRenderer}, a stateless
|
||||
* class - that could/should be used as a Singleton with the static method
|
||||
* {@code goog.ui.media.Mp3.getInstance} -, that knows how to render Mp3s. It is
|
||||
* designed to be used with a {@link goog.ui.Control}, which will actually
|
||||
* control the media renderer and provide the {@link goog.ui.Component} base.
|
||||
* This design guarantees that all different types of medias will behave alike
|
||||
* but will look different.
|
||||
*
|
||||
* goog.ui.media.Mp3 expects mp3 urls on {@code goog.ui.Control.getModel} as
|
||||
* data models, and render a flash object that will play that URL.
|
||||
*
|
||||
* Example of usage:
|
||||
*
|
||||
* <pre>
|
||||
* goog.ui.media.Mp3.newControl('http://hostname/file.mp3').render();
|
||||
* </pre>
|
||||
*
|
||||
* Mp3 medias currently support the following states:
|
||||
*
|
||||
* <ul>
|
||||
* <li> {@link goog.ui.Component.State.DISABLED}: shows 'flash not available'
|
||||
* <li> {@link goog.ui.Component.State.HOVER}: mouse cursor is over the mp3
|
||||
* <li> {@link goog.ui.Component.State.SELECTED}: mp3 is playing
|
||||
* </ul>
|
||||
*
|
||||
* Which can be accessed by
|
||||
*
|
||||
* <pre>
|
||||
* mp3.setEnabled(true);
|
||||
* mp3.setHighlighted(true);
|
||||
* mp3.setSelected(true);
|
||||
* </pre>
|
||||
*
|
||||
*
|
||||
* @supported IE6, FF2+, Safari. Requires flash to actually work.
|
||||
*
|
||||
* TODO(user): test on other browsers
|
||||
*/
|
||||
|
||||
goog.provide('goog.ui.media.Mp3');
|
||||
|
||||
goog.require('goog.string');
|
||||
goog.require('goog.ui.media.FlashObject');
|
||||
goog.require('goog.ui.media.Media');
|
||||
goog.require('goog.ui.media.MediaRenderer');
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Subclasses a goog.ui.media.MediaRenderer to provide a Mp3 specific media
|
||||
* renderer.
|
||||
*
|
||||
* This class knows how to parse mp3 URLs, and render the DOM structure
|
||||
* of mp3 flash players. This class is meant to be used as a singleton static
|
||||
* stateless class, that takes {@code goog.ui.media.Media} instances and renders
|
||||
* it. It expects {@code goog.ui.media.Media.getModel} to return a well formed,
|
||||
* previously checked, mp3 URL {@see goog.ui.media.PicasaAlbum.parseUrl},
|
||||
* which is the data model this renderer will use to construct the DOM
|
||||
* structure. {@see goog.ui.media.PicasaAlbum.newControl} for an example of
|
||||
* constructing a control with this renderer.
|
||||
*
|
||||
* This design is patterned after http://go/closure_control_subclassing
|
||||
*
|
||||
* It uses {@link goog.ui.media.FlashObject} to embed the flash object.
|
||||
*
|
||||
* @constructor
|
||||
* @extends {goog.ui.media.MediaRenderer}
|
||||
* @final
|
||||
*/
|
||||
goog.ui.media.Mp3 = function() {
|
||||
goog.ui.media.MediaRenderer.call(this);
|
||||
};
|
||||
goog.inherits(goog.ui.media.Mp3, goog.ui.media.MediaRenderer);
|
||||
goog.addSingletonGetter(goog.ui.media.Mp3);
|
||||
|
||||
|
||||
/**
|
||||
* Flash player arguments. We expect that {@code flashUrl_} will contain a flash
|
||||
* movie that takes an audioUrl parameter on its URL, containing the URL of the
|
||||
* mp3 to be played.
|
||||
*
|
||||
* @type {string}
|
||||
* @private
|
||||
*/
|
||||
goog.ui.media.Mp3.PLAYER_ARGUMENTS_ = 'audioUrl=%s';
|
||||
|
||||
|
||||
/**
|
||||
* Default CSS class to be applied to the root element of components rendered
|
||||
* by this renderer.
|
||||
*
|
||||
* @type {string}
|
||||
*/
|
||||
goog.ui.media.Mp3.CSS_CLASS = goog.getCssName('goog-ui-media-mp3');
|
||||
|
||||
|
||||
/**
|
||||
* Flash player URL. Uses Google Reader's mp3 flash player by default.
|
||||
*
|
||||
* @type {string}
|
||||
* @private
|
||||
*/
|
||||
goog.ui.media.Mp3.flashUrl_ =
|
||||
'http://www.google.com/reader/ui/3523697345-audio-player.swf';
|
||||
|
||||
|
||||
/**
|
||||
* Regular expression to check if a given URL is a valid mp3 URL.
|
||||
*
|
||||
* Copied from http://go/markdownlite.js.
|
||||
|
||||
*
|
||||
* NOTE(user): although it would be easier to use goog.string.endsWith('.mp3'),
|
||||
* in the future, we want to provide media inlining, which is basically getting
|
||||
* a text and replacing all mp3 references with an mp3 player, so it makes sense
|
||||
* to share the same regular expression to match everything.
|
||||
*
|
||||
* @type {RegExp}
|
||||
*/
|
||||
goog.ui.media.Mp3.MATCHER =
|
||||
/(https?:\/\/[\w-%&\/.=:#\+~\(\)]+\.(mp3)+(\?[\w-%&\/.=:#\+~\(\)]+)?)/i;
|
||||
|
||||
|
||||
/**
|
||||
* A static convenient method to construct a goog.ui.media.Media control out of
|
||||
* a mp3 URL. It checks the mp3 URL, sets it as the data model
|
||||
* goog.ui.media.Mp3 renderer uses, sets the states supported by the renderer,
|
||||
* and returns a Control that binds everything together. This is what you
|
||||
* should be using for constructing Mp3 videos, except if you need more fine
|
||||
* control over the configuration.
|
||||
*
|
||||
* @param {goog.ui.media.MediaModel} dataModel A media model that must contain
|
||||
* an mp3 url on {@code dataModel.getUrl}.
|
||||
* @param {goog.dom.DomHelper=} opt_domHelper Optional DOM helper, used for
|
||||
* document interaction.
|
||||
* @return {!goog.ui.media.Media} A goog.ui.Control subclass with the mp3
|
||||
* renderer.
|
||||
*/
|
||||
goog.ui.media.Mp3.newControl = function(dataModel, opt_domHelper) {
|
||||
var control = new goog.ui.media.Media(
|
||||
dataModel,
|
||||
goog.ui.media.Mp3.getInstance(),
|
||||
opt_domHelper);
|
||||
// mp3 ui doesn't have a non selected view: it shows the mp3 player by
|
||||
// default.
|
||||
control.setSelected(true);
|
||||
return control;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* A static method that sets which flash URL this class should use. Use this if
|
||||
* you want to host your own flash mp3 player.
|
||||
*
|
||||
* @param {string} flashUrl The URL of the flash mp3 player.
|
||||
*/
|
||||
goog.ui.media.Mp3.setFlashUrl = function(flashUrl) {
|
||||
goog.ui.media.Mp3.flashUrl_ = flashUrl;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* A static method that builds a URL that will contain the flash player that
|
||||
* will play the {@code mp3Url}.
|
||||
*
|
||||
* @param {string} mp3Url The URL of the mp3 music.
|
||||
* @return {string} An URL of a flash player that will know how to play the
|
||||
* given {@code mp3Url}.
|
||||
*/
|
||||
goog.ui.media.Mp3.buildFlashUrl = function(mp3Url) {
|
||||
var flashUrl = goog.ui.media.Mp3.flashUrl_ + '?' + goog.string.subs(
|
||||
goog.ui.media.Mp3.PLAYER_ARGUMENTS_,
|
||||
goog.string.urlEncode(mp3Url));
|
||||
return flashUrl;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Creates the initial DOM structure of a mp3 video, which is basically a
|
||||
* the flash object pointing to a flash mp3 player.
|
||||
*
|
||||
* @param {goog.ui.Control} c The media control.
|
||||
* @return {!Element} A DOM structure that represents the control.
|
||||
* @override
|
||||
*/
|
||||
goog.ui.media.Mp3.prototype.createDom = function(c) {
|
||||
var control = /** @type {goog.ui.media.Media} */ (c);
|
||||
var div = goog.ui.media.Mp3.superClass_.createDom.call(this, control);
|
||||
|
||||
var dataModel =
|
||||
/** @type {goog.ui.media.MediaModel} */ (control.getDataModel());
|
||||
var flash = new goog.ui.media.FlashObject(
|
||||
dataModel.getPlayer().getTrustedResourceUrl(), control.getDomHelper());
|
||||
flash.setFlashVar('playerMode', 'embedded');
|
||||
flash.render(div);
|
||||
|
||||
return div;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Returns the CSS class to be applied to the root element of components
|
||||
* rendered using this renderer.
|
||||
* @return {string} Renderer-specific CSS class.
|
||||
* @override
|
||||
*/
|
||||
goog.ui.media.Mp3.prototype.getCssClass = function() {
|
||||
return goog.ui.media.Mp3.CSS_CLASS;
|
||||
};
|
||||
@@ -0,0 +1,24 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<!--
|
||||
Copyright 2009 The Closure Library Authors. All Rights Reserved.
|
||||
|
||||
Use of this source code is governed by the Apache License, Version 2.0.
|
||||
See the COPYING file for details.
|
||||
-->
|
||||
<!--
|
||||
-->
|
||||
<head>
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
||||
<title>
|
||||
Closure Unit Tests - goog.ui.media.Mp3
|
||||
</title>
|
||||
<script src="../../base.js">
|
||||
</script>
|
||||
<script>
|
||||
goog.require('goog.ui.media.Mp3Test');
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,66 @@
|
||||
// Copyright 2009 The Closure Library Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS-IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
goog.provide('goog.ui.media.Mp3Test');
|
||||
goog.setTestOnly('goog.ui.media.Mp3Test');
|
||||
|
||||
goog.require('goog.dom');
|
||||
goog.require('goog.testing.jsunit');
|
||||
goog.require('goog.ui.media.FlashObject');
|
||||
goog.require('goog.ui.media.Media');
|
||||
goog.require('goog.ui.media.MediaModel');
|
||||
goog.require('goog.ui.media.Mp3');
|
||||
var mp3;
|
||||
var control;
|
||||
var MP3_URL = 'http://www.shellworld.net/~davidsky/surf-oxy.mp3';
|
||||
var parent = goog.dom.createElement('div');
|
||||
|
||||
function setUp() {
|
||||
mp3 = goog.ui.media.Mp3.getInstance();
|
||||
var flashUrl = goog.ui.media.Mp3.buildFlashUrl(MP3_URL);
|
||||
var model = new goog.ui.media.MediaModel(MP3_URL, 'mp3 caption', '');
|
||||
model.setPlayer(new goog.ui.media.MediaModel.Player(flashUrl));
|
||||
control = new goog.ui.media.Media(model, mp3);
|
||||
control.setSelected(true);
|
||||
}
|
||||
|
||||
function tearDown() {
|
||||
control.dispose();
|
||||
}
|
||||
|
||||
function testBasicRendering() {
|
||||
control.render(parent);
|
||||
var el = goog.dom.getElementsByTagNameAndClass(
|
||||
'div', goog.ui.media.Mp3.CSS_CLASS, parent);
|
||||
assertEquals(1, el.length);
|
||||
}
|
||||
|
||||
function testParsingUrl() {
|
||||
assertTrue(goog.ui.media.Mp3.MATCHER.test(MP3_URL));
|
||||
assertFalse(
|
||||
goog.ui.media.Mp3.MATCHER.test('http://invalidUrl/filename.doc'));
|
||||
}
|
||||
|
||||
function testCreatingDomOnInitialState() {
|
||||
control.render(parent);
|
||||
var caption = goog.dom.getElementsByTagNameAndClass(
|
||||
'div',
|
||||
goog.ui.media.Mp3.CSS_CLASS + '-caption',
|
||||
parent);
|
||||
assertEquals(1, caption.length);
|
||||
|
||||
var flash = goog.dom.getElementsByTagNameAndClass(
|
||||
undefined, goog.ui.media.FlashObject.CSS_CLASS, parent);
|
||||
assertEquals(1, flash.length);
|
||||
}
|
||||
@@ -0,0 +1,143 @@
|
||||
// Copyright 2009 The Closure Library Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS-IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
/**
|
||||
* @fileoverview provides a reusable photo UI component that renders photos that
|
||||
* contains metadata (such as captions, description, thumbnail/high resolution
|
||||
* versions, etc).
|
||||
*
|
||||
* goog.ui.media.Photo is actually a {@link goog.ui.ControlRenderer},
|
||||
* a stateless class - that could/should be used as a Singleton with the static
|
||||
* method {@code goog.ui.media.Photo.getInstance} -, that knows how to render
|
||||
* Photos. It is designed to be used with a {@link goog.ui.Control}, which will
|
||||
* actually control the media renderer and provide the {@link goog.ui.Component}
|
||||
* base. This design guarantees that all different types of medias will behave
|
||||
* alike but will look different.
|
||||
*
|
||||
* goog.ui.media.Photo expects {@code goog.ui.media.MediaModel} on
|
||||
* {@code goog.ui.Control.getModel} as data models.
|
||||
*
|
||||
* Example of usage:
|
||||
*
|
||||
* <pre>
|
||||
* var photo = goog.ui.media.Photo.newControl(
|
||||
* new goog.ui.media.MediaModel('http://hostname/file.jpg'));
|
||||
* photo.render(goog.dom.getElement('parent'));
|
||||
* </pre>
|
||||
*
|
||||
* Photo medias currently support the following states:
|
||||
*
|
||||
* <ul>
|
||||
* <li> {@link goog.ui.Component.State.HOVER}: mouse cursor is over the photo.
|
||||
* <li> {@link goog.ui.Component.State.SELECTED}: photo is being displayed.
|
||||
* </ul>
|
||||
*
|
||||
* Which can be accessed by
|
||||
*
|
||||
* <pre>
|
||||
* photo.setHighlighted(true);
|
||||
* photo.setSelected(true);
|
||||
* </pre>
|
||||
*
|
||||
*/
|
||||
|
||||
goog.provide('goog.ui.media.Photo');
|
||||
|
||||
goog.require('goog.ui.media.Media');
|
||||
goog.require('goog.ui.media.MediaRenderer');
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Subclasses a goog.ui.media.MediaRenderer to provide a Photo specific media
|
||||
* renderer. Provides a base class for any other renderer that wants to display
|
||||
* photos.
|
||||
*
|
||||
* This class is meant to be used as a singleton static stateless class, that
|
||||
* takes {@code goog.ui.media.Media} instances and renders it.
|
||||
*
|
||||
* This design is patterned after
|
||||
* http://go/closure_control_subclassing
|
||||
*
|
||||
* @constructor
|
||||
* @extends {goog.ui.media.MediaRenderer}
|
||||
* @final
|
||||
*/
|
||||
goog.ui.media.Photo = function() {
|
||||
goog.ui.media.MediaRenderer.call(this);
|
||||
};
|
||||
goog.inherits(goog.ui.media.Photo, goog.ui.media.MediaRenderer);
|
||||
goog.addSingletonGetter(goog.ui.media.Photo);
|
||||
|
||||
|
||||
/**
|
||||
* Default CSS class to be applied to the root element of components rendered
|
||||
* by this renderer.
|
||||
*
|
||||
* @type {string}
|
||||
*/
|
||||
goog.ui.media.Photo.CSS_CLASS = goog.getCssName('goog-ui-media-photo');
|
||||
|
||||
|
||||
/**
|
||||
* A static convenient method to construct a goog.ui.media.Media control out of
|
||||
* a photo {@code goog.ui.media.MediaModel}. It sets it as the data model
|
||||
* goog.ui.media.Photo renderer uses, sets the states supported by the renderer,
|
||||
* and returns a Control that binds everything together. This is what you
|
||||
* should be using for constructing Photos, except if you need finer control
|
||||
* over the configuration.
|
||||
*
|
||||
* @param {goog.ui.media.MediaModel} dataModel The photo data model.
|
||||
* @return {!goog.ui.media.Media} A goog.ui.Control subclass with the photo
|
||||
* renderer.
|
||||
*/
|
||||
goog.ui.media.Photo.newControl = function(dataModel) {
|
||||
var control = new goog.ui.media.Media(
|
||||
dataModel,
|
||||
goog.ui.media.Photo.getInstance());
|
||||
return control;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Creates the initial DOM structure of a photo.
|
||||
*
|
||||
* @param {goog.ui.Control} c The media control.
|
||||
* @return {!Element} A DOM structure that represents the control.
|
||||
* @override
|
||||
*/
|
||||
goog.ui.media.Photo.prototype.createDom = function(c) {
|
||||
var control = /** @type {goog.ui.media.Media} */ (c);
|
||||
var div = goog.ui.media.Photo.superClass_.createDom.call(this, control);
|
||||
|
||||
var img = control.getDomHelper().createDom('img', {
|
||||
src: control.getDataModel().getPlayer().getUrl(),
|
||||
className: goog.getCssName(this.getCssClass(), 'image')
|
||||
});
|
||||
|
||||
div.appendChild(img);
|
||||
|
||||
return div;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Returns the CSS class to be applied to the root element of components
|
||||
* rendered using this renderer.
|
||||
* @return {string} Renderer-specific CSS class.
|
||||
* @override
|
||||
*/
|
||||
goog.ui.media.Photo.prototype.getCssClass = function() {
|
||||
return goog.ui.media.Photo.CSS_CLASS;
|
||||
};
|
||||
@@ -0,0 +1,24 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<!--
|
||||
Copyright 2009 The Closure Library Authors. All Rights Reserved.
|
||||
|
||||
Use of this source code is governed by the Apache License, Version 2.0.
|
||||
See the COPYING file for details.
|
||||
-->
|
||||
<!--
|
||||
-->
|
||||
<head>
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
||||
<title>
|
||||
Closure Unit Tests - goog.ui.media.Photo
|
||||
</title>
|
||||
<script src="../../base.js">
|
||||
</script>
|
||||
<script>
|
||||
goog.require('goog.ui.media.PhotoTest');
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,49 @@
|
||||
// Copyright 2009 The Closure Library Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS-IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
goog.provide('goog.ui.media.PhotoTest');
|
||||
goog.setTestOnly('goog.ui.media.PhotoTest');
|
||||
|
||||
goog.require('goog.dom');
|
||||
goog.require('goog.testing.jsunit');
|
||||
goog.require('goog.ui.media.MediaModel');
|
||||
goog.require('goog.ui.media.Photo');
|
||||
var control;
|
||||
var PHOTO_URL = 'http://foo/bar.jpg';
|
||||
|
||||
function setUp() {
|
||||
var photo = new goog.ui.media.MediaModel(PHOTO_URL, 'title', 'description');
|
||||
photo.setPlayer(new goog.ui.media.MediaModel.Player(PHOTO_URL));
|
||||
control = goog.ui.media.Photo.newControl(photo);
|
||||
}
|
||||
|
||||
function tearDown() {
|
||||
control.dispose();
|
||||
}
|
||||
|
||||
function testBasicRendering() {
|
||||
control.render();
|
||||
var el = goog.dom.getElementsByTagNameAndClass('div',
|
||||
goog.ui.media.Photo.CSS_CLASS);
|
||||
assertEquals(1, el.length);
|
||||
var img = goog.dom.getElementsByTagNameAndClass('img',
|
||||
goog.ui.media.Photo.CSS_CLASS + '-image');
|
||||
assertEquals(1, img.length);
|
||||
var caption = goog.dom.getElementsByTagNameAndClass('div',
|
||||
goog.ui.media.Photo.CSS_CLASS + '-caption');
|
||||
assertEquals(1, caption.length);
|
||||
var content = goog.dom.getElementsByTagNameAndClass('div',
|
||||
goog.ui.media.Photo.CSS_CLASS + '-description');
|
||||
assertEquals(1, content.length);
|
||||
}
|
||||
@@ -0,0 +1,327 @@
|
||||
// Copyright 2009 The Closure Library Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS-IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
|
||||
/**
|
||||
* @fileoverview provides a reusable picasa album UI component given a public
|
||||
* picasa album URL.
|
||||
*
|
||||
* TODO(user): implement the javascript viewer, for users without flash. Get it
|
||||
* from the Gmail Picasa gadget.
|
||||
*
|
||||
* goog.ui.media.PicasaAlbum is actually a {@link goog.ui.ControlRenderer}, a
|
||||
* stateless class - that could/should be used as a Singleton with the static
|
||||
* method {@code goog.ui.media.PicasaAlbum.getInstance} -, that knows how to
|
||||
* render picasa albums. It is designed to be used with a
|
||||
* {@link goog.ui.Control}, which will actually control the media renderer and
|
||||
* provide the {@link goog.ui.Component} base. This design guarantees that all
|
||||
* different types of medias will behave alike but will look different.
|
||||
*
|
||||
* goog.ui.media.PicasaAlbum expects {@code goog.ui.media.PicasaAlbumModel}s on
|
||||
* {@code goog.ui.Control.getModel} as data models, and render a flash object
|
||||
* that will show a slideshow with the contents of that album URL.
|
||||
*
|
||||
* Example of usage:
|
||||
*
|
||||
* <pre>
|
||||
* var album = goog.ui.media.PicasaAlbumModel.newInstance(
|
||||
* 'http://picasaweb.google.com/username/SanFranciscoCalifornia');
|
||||
* goog.ui.media.PicasaAlbum.newControl(album).render();
|
||||
* </pre>
|
||||
*
|
||||
* picasa medias currently support the following states:
|
||||
*
|
||||
* <ul>
|
||||
* <li> {@link goog.ui.Component.State.DISABLED}: shows 'flash not available'
|
||||
* <li> {@link goog.ui.Component.State.HOVER}: mouse cursor is over the album
|
||||
* <li> {@link goog.ui.Component.State.SELECTED}: flash album is shown
|
||||
* </ul>
|
||||
*
|
||||
* Which can be accessed by
|
||||
*
|
||||
* <pre>
|
||||
* picasa.setEnabled(true);
|
||||
* picasa.setHighlighted(true);
|
||||
* picasa.setSelected(true);
|
||||
* </pre>
|
||||
*
|
||||
*
|
||||
* @supported IE6, FF2+, Safari. Requires flash to actually work.
|
||||
*
|
||||
* TODO(user): test on other browsers
|
||||
*/
|
||||
|
||||
goog.provide('goog.ui.media.PicasaAlbum');
|
||||
goog.provide('goog.ui.media.PicasaAlbumModel');
|
||||
|
||||
goog.require('goog.html.TrustedResourceUrl');
|
||||
goog.require('goog.string.Const');
|
||||
goog.require('goog.ui.media.FlashObject');
|
||||
goog.require('goog.ui.media.Media');
|
||||
goog.require('goog.ui.media.MediaModel');
|
||||
goog.require('goog.ui.media.MediaRenderer');
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Subclasses a goog.ui.media.MediaRenderer to provide a Picasa specific media
|
||||
* renderer.
|
||||
*
|
||||
* This class knows how to parse picasa URLs, and render the DOM structure
|
||||
* of picasa album players and previews. This class is meant to be used as a
|
||||
* singleton static stateless class, that takes {@code goog.ui.media.Media}
|
||||
* instances and renders it. It expects {@code goog.ui.media.Media.getModel} to
|
||||
* return a well formed, previously constructed, object with a user and album
|
||||
* fields {@see goog.ui.media.PicasaAlbum.parseUrl}, which is the data model
|
||||
* this renderer will use to construct the DOM structure.
|
||||
* {@see goog.ui.media.PicasaAlbum.newControl} for a example of constructing a
|
||||
* control with this renderer.
|
||||
*
|
||||
* goog.ui.media.PicasaAlbum currently displays a picasa-made flash slideshow
|
||||
* with the photos, but could possibly display a handwritten js photo viewer,
|
||||
* in case flash is not available.
|
||||
*
|
||||
* This design is patterned after http://go/closure_control_subclassing
|
||||
*
|
||||
* It uses {@link goog.ui.media.FlashObject} to embed the flash object.
|
||||
*
|
||||
* @constructor
|
||||
* @extends {goog.ui.media.MediaRenderer}
|
||||
* @final
|
||||
*/
|
||||
goog.ui.media.PicasaAlbum = function() {
|
||||
goog.ui.media.MediaRenderer.call(this);
|
||||
};
|
||||
goog.inherits(goog.ui.media.PicasaAlbum, goog.ui.media.MediaRenderer);
|
||||
goog.addSingletonGetter(goog.ui.media.PicasaAlbum);
|
||||
|
||||
|
||||
/**
|
||||
* Default CSS class to be applied to the root element of components rendered
|
||||
* by this renderer.
|
||||
*
|
||||
* @type {string}
|
||||
*/
|
||||
goog.ui.media.PicasaAlbum.CSS_CLASS = goog.getCssName('goog-ui-media-picasa');
|
||||
|
||||
|
||||
/**
|
||||
* A static convenient method to construct a goog.ui.media.Media control out of
|
||||
* a picasa data model. It sets it as the data model goog.ui.media.PicasaAlbum
|
||||
* renderer uses, sets the states supported by the renderer, and returns a
|
||||
* Control that binds everything together. This is what you should be using for
|
||||
* constructing Picasa albums, except if you need finer control over the
|
||||
* configuration.
|
||||
*
|
||||
* @param {goog.ui.media.PicasaAlbumModel} dataModel A picasa album data model.
|
||||
* @param {goog.dom.DomHelper=} opt_domHelper Optional DOM helper, used for
|
||||
* document interaction.
|
||||
* @return {!goog.ui.media.Media} A Control instance binded to the Picasa
|
||||
* renderer.
|
||||
*/
|
||||
goog.ui.media.PicasaAlbum.newControl = function(dataModel, opt_domHelper) {
|
||||
var control = new goog.ui.media.Media(
|
||||
dataModel,
|
||||
goog.ui.media.PicasaAlbum.getInstance(),
|
||||
opt_domHelper);
|
||||
control.setSelected(true);
|
||||
return control;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Creates the initial DOM structure of the picasa album, which is basically a
|
||||
* the flash object pointing to a flash picasa album player.
|
||||
*
|
||||
* @param {goog.ui.Control} c The media control.
|
||||
* @return {!Element} The DOM structure that represents the control.
|
||||
* @override
|
||||
*/
|
||||
goog.ui.media.PicasaAlbum.prototype.createDom = function(c) {
|
||||
var control = /** @type {goog.ui.media.Media} */ (c);
|
||||
var div = goog.ui.media.PicasaAlbum.superClass_.createDom.call(this, control);
|
||||
|
||||
var picasaAlbum =
|
||||
/** @type {goog.ui.media.PicasaAlbumModel} */ (control.getDataModel());
|
||||
var authParam =
|
||||
picasaAlbum.getAuthKey() ? ('&authkey=' + picasaAlbum.getAuthKey()) : '';
|
||||
var flash = new goog.ui.media.FlashObject(
|
||||
picasaAlbum.getPlayer().getTrustedResourceUrl(),
|
||||
control.getDomHelper());
|
||||
flash.addFlashVars(picasaAlbum.getPlayer().getVars());
|
||||
flash.render(div);
|
||||
|
||||
return div;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Returns the CSS class to be applied to the root element of components
|
||||
* rendered using this renderer.
|
||||
* @return {string} Renderer-specific CSS class.
|
||||
* @override
|
||||
*/
|
||||
goog.ui.media.PicasaAlbum.prototype.getCssClass = function() {
|
||||
return goog.ui.media.PicasaAlbum.CSS_CLASS;
|
||||
};
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* The {@code goog.ui.media.PicasaAlbum} media data model. It stores a required
|
||||
* {@code userId} and {@code albumId} fields, sets the picasa album URL, and
|
||||
* allows a few optional parameters.
|
||||
*
|
||||
* @param {string} userId The picasa userId associated with this album.
|
||||
* @param {string} albumId The picasa albumId associated with this album.
|
||||
* @param {string=} opt_authKey An optional authentication key, used on private
|
||||
* albums.
|
||||
* @param {string=} opt_caption An optional caption of the picasa album.
|
||||
* @param {string=} opt_description An optional description of the picasa album.
|
||||
* @param {boolean=} opt_autoplay Whether to autoplay the slideshow.
|
||||
* @constructor
|
||||
* @extends {goog.ui.media.MediaModel}
|
||||
* @final
|
||||
*/
|
||||
goog.ui.media.PicasaAlbumModel = function(userId,
|
||||
albumId,
|
||||
opt_authKey,
|
||||
opt_caption,
|
||||
opt_description,
|
||||
opt_autoplay) {
|
||||
goog.ui.media.MediaModel.call(
|
||||
this,
|
||||
goog.ui.media.PicasaAlbumModel.buildUrl(userId, albumId),
|
||||
opt_caption,
|
||||
opt_description,
|
||||
goog.ui.media.MediaModel.MimeType.FLASH);
|
||||
|
||||
/**
|
||||
* The Picasa user id.
|
||||
* @type {string}
|
||||
* @private
|
||||
*/
|
||||
this.userId_ = userId;
|
||||
|
||||
/**
|
||||
* The Picasa album id.
|
||||
* @type {string}
|
||||
* @private
|
||||
*/
|
||||
this.albumId_ = albumId;
|
||||
|
||||
/**
|
||||
* The Picasa authentication key, used on private albums.
|
||||
* @type {?string}
|
||||
* @private
|
||||
*/
|
||||
this.authKey_ = opt_authKey || null;
|
||||
|
||||
var authParam = opt_authKey ? ('&authkey=' + opt_authKey) : '';
|
||||
|
||||
var flashVars = {
|
||||
'host': 'picasaweb.google.com',
|
||||
'RGB': '0x000000',
|
||||
'feed': 'http://picasaweb.google.com/data/feed/api/user/' +
|
||||
userId + '/album/' + albumId + '?kind=photo&alt=rss' + authParam
|
||||
};
|
||||
flashVars[opt_autoplay ? 'autoplay' : 'noautoplay'] = '1';
|
||||
|
||||
var flashUrl = goog.html.TrustedResourceUrl.fromConstant(
|
||||
goog.string.Const.from(
|
||||
'http://picasaweb.google.com/s/c/bin/slideshow.swf'));
|
||||
var player = new goog.ui.media.MediaModel.Player(flashUrl, flashVars);
|
||||
|
||||
this.setPlayer(player);
|
||||
};
|
||||
goog.inherits(goog.ui.media.PicasaAlbumModel, goog.ui.media.MediaModel);
|
||||
|
||||
|
||||
/**
|
||||
* Regular expression used to extract the picasa username and albumid out of
|
||||
* picasa URLs.
|
||||
*
|
||||
* Copied from http://go/markdownlite.js,
|
||||
* and {@link PicasaWebExtractor.xml}.
|
||||
*
|
||||
* @type {RegExp}
|
||||
* @private
|
||||
* @const
|
||||
*/
|
||||
goog.ui.media.PicasaAlbumModel.MATCHER_ =
|
||||
/https?:\/\/(?:www\.)?picasaweb\.(?:google\.)?com\/([\d\w\.]+)\/([\d\w_\-\.]+)(?:\?[\w\d\-_=&;\.]*&?authKey=([\w\d\-_=;\.]+))?(?:#([\d]+)?)?/im;
|
||||
|
||||
|
||||
/**
|
||||
* Gets a {@code picasaUrl} and extracts the user and album id.
|
||||
*
|
||||
* @param {string} picasaUrl A picasa album URL.
|
||||
* @param {string=} opt_caption An optional caption of the picasa album.
|
||||
* @param {string=} opt_description An optional description of the picasa album.
|
||||
* @param {boolean=} opt_autoplay Whether to autoplay the slideshow.
|
||||
* @return {!goog.ui.media.PicasaAlbumModel} The picasa album data model that
|
||||
* represents the picasa URL.
|
||||
* @throws exception in case the parsing fails
|
||||
*/
|
||||
goog.ui.media.PicasaAlbumModel.newInstance = function(picasaUrl,
|
||||
opt_caption,
|
||||
opt_description,
|
||||
opt_autoplay) {
|
||||
if (goog.ui.media.PicasaAlbumModel.MATCHER_.test(picasaUrl)) {
|
||||
var data = goog.ui.media.PicasaAlbumModel.MATCHER_.exec(picasaUrl);
|
||||
return new goog.ui.media.PicasaAlbumModel(
|
||||
data[1], data[2], data[3], opt_caption, opt_description, opt_autoplay);
|
||||
}
|
||||
throw Error('failed to parse user and album from picasa url: ' + picasaUrl);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* The opposite of {@code newInstance}: takes an {@code userId} and an
|
||||
* {@code albumId} and builds a URL.
|
||||
*
|
||||
* @param {string} userId The user that owns the album.
|
||||
* @param {string} albumId The album id.
|
||||
* @return {string} The URL of the album.
|
||||
*/
|
||||
goog.ui.media.PicasaAlbumModel.buildUrl = function(userId, albumId) {
|
||||
return 'http://picasaweb.google.com/' + userId + '/' + albumId;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Gets the Picasa user id.
|
||||
* @return {string} The Picasa user id.
|
||||
*/
|
||||
goog.ui.media.PicasaAlbumModel.prototype.getUserId = function() {
|
||||
return this.userId_;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Gets the Picasa album id.
|
||||
* @return {string} The Picasa album id.
|
||||
*/
|
||||
goog.ui.media.PicasaAlbumModel.prototype.getAlbumId = function() {
|
||||
return this.albumId_;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Gets the Picasa album authentication key.
|
||||
* @return {?string} The Picasa album authentication key.
|
||||
*/
|
||||
goog.ui.media.PicasaAlbumModel.prototype.getAuthKey = function() {
|
||||
return this.authKey_;
|
||||
};
|
||||
@@ -0,0 +1,24 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<!--
|
||||
Copyright 2009 The Closure Library Authors. All Rights Reserved.
|
||||
|
||||
Use of this source code is governed by the Apache License, Version 2.0.
|
||||
See the COPYING file for details.
|
||||
-->
|
||||
<!--
|
||||
-->
|
||||
<head>
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
||||
<title>
|
||||
Closure Unit Tests - goog.ui.media.Picasa
|
||||
</title>
|
||||
<script src="../../base.js">
|
||||
</script>
|
||||
<script>
|
||||
goog.require('goog.ui.media.PicasaTest');
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,110 @@
|
||||
// Copyright 2009 The Closure Library Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS-IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
goog.provide('goog.ui.media.PicasaTest');
|
||||
goog.setTestOnly('goog.ui.media.PicasaTest');
|
||||
|
||||
goog.require('goog.dom');
|
||||
goog.require('goog.testing.jsunit');
|
||||
goog.require('goog.ui.media.FlashObject');
|
||||
goog.require('goog.ui.media.Media');
|
||||
goog.require('goog.ui.media.PicasaAlbum');
|
||||
goog.require('goog.ui.media.PicasaAlbumModel');
|
||||
var picasa;
|
||||
var control;
|
||||
var PICASA_USERNAME = 'username';
|
||||
var PICASA_ALBUM = 'albumname';
|
||||
var PICASA_URL = 'http://picasaweb.google.com/' + PICASA_USERNAME + '/' +
|
||||
PICASA_ALBUM;
|
||||
var parent = goog.dom.createElement('div');
|
||||
|
||||
function setUp() {
|
||||
picasa = new goog.ui.media.PicasaAlbum();
|
||||
var model = new goog.ui.media.PicasaAlbumModel(PICASA_USERNAME,
|
||||
PICASA_ALBUM, null, 'album title');
|
||||
control = new goog.ui.media.Media(model, picasa);
|
||||
control.setSelected(true);
|
||||
}
|
||||
|
||||
function tearDown() {
|
||||
control.dispose();
|
||||
}
|
||||
|
||||
function testBasicRendering() {
|
||||
control.render(parent);
|
||||
var el = goog.dom.getElementsByTagNameAndClass(
|
||||
'div', goog.ui.media.PicasaAlbum.CSS_CLASS, parent);
|
||||
assertEquals(1, el.length);
|
||||
assertEquals(PICASA_URL, control.getDataModel().getUrl());
|
||||
}
|
||||
|
||||
function testParsingUrl() {
|
||||
assertExtractsCorrectly(PICASA_USERNAME, PICASA_ALBUM, null, PICASA_URL);
|
||||
assertExtractsCorrectly('foo', 'bar', null,
|
||||
'https://picasaweb.google.com/foo/bar');
|
||||
assertExtractsCorrectly('foo', 'bar', null,
|
||||
'https://www.picasaweb.google.com/foo/bar');
|
||||
assertExtractsCorrectly('foo', 'bar', null,
|
||||
'https://www.picasaweb.com/foo/bar');
|
||||
assertExtractsCorrectly('foo', 'bar', '8Hzg1CUUAZM',
|
||||
'https://www.picasaweb.com/foo/bar?authkey=8Hzg1CUUAZM#');
|
||||
assertExtractsCorrectly('foo', 'bar', '8Hzg1CUUAZM',
|
||||
'https://www.picasaweb.com/foo/bar?foo=bar&authkey=8Hzg1CUUAZM#');
|
||||
assertExtractsCorrectly('foo', 'bar', '8Hzg1CUUAZM',
|
||||
'https://www.picasaweb.com/foo/bar?foo=bar&authkey=8Hzg1CUUAZM&' +
|
||||
'hello=world#');
|
||||
|
||||
var invalidUrl = 'http://invalidUrl/watch?v=dMH0bHeiRNg';
|
||||
var e = assertThrows('parser expects a well formed URL', function() {
|
||||
goog.ui.media.PicasaAlbumModel.newInstance(invalidUrl);
|
||||
});
|
||||
assertEquals(
|
||||
'failed to parse user and album from picasa url: ' + invalidUrl,
|
||||
e.message);
|
||||
}
|
||||
|
||||
function testBuildingUrl() {
|
||||
assertEquals(PICASA_URL,
|
||||
goog.ui.media.PicasaAlbumModel.buildUrl(PICASA_USERNAME, PICASA_ALBUM));
|
||||
}
|
||||
|
||||
function testCreatingModel() {
|
||||
var model = new goog.ui.media.PicasaAlbumModel(
|
||||
PICASA_USERNAME, PICASA_ALBUM);
|
||||
assertEquals(PICASA_USERNAME, model.getUserId());
|
||||
assertEquals(PICASA_ALBUM, model.getAlbumId());
|
||||
assertEquals(PICASA_URL, model.getUrl());
|
||||
assertUndefined(model.getCaption());
|
||||
}
|
||||
|
||||
function testCreatingDomOnInitialState() {
|
||||
control.render(parent);
|
||||
var caption = goog.dom.getElementsByTagNameAndClass(
|
||||
'div',
|
||||
goog.ui.media.PicasaAlbum.CSS_CLASS + '-caption',
|
||||
parent);
|
||||
assertEquals(1, caption.length);
|
||||
|
||||
var flash = goog.dom.getElementsByTagNameAndClass(
|
||||
'div', goog.ui.media.FlashObject.CSS_CLASS, parent);
|
||||
assertEquals(1, flash.length);
|
||||
}
|
||||
|
||||
function assertExtractsCorrectly(
|
||||
expectedUserId, expectedAlbumId, expectedAuthKey, url) {
|
||||
var model = goog.ui.media.PicasaAlbumModel.newInstance(url);
|
||||
assertEquals('User for ' + url, expectedUserId, model.getUserId());
|
||||
assertEquals('Album for ' + url, expectedAlbumId, model.getAlbumId());
|
||||
assertEquals('AuthKey for ' + url, expectedAuthKey, model.getAuthKey());
|
||||
}
|
||||
@@ -0,0 +1,278 @@
|
||||
// Copyright 2009 The Closure Library Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS-IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
|
||||
/**
|
||||
* @fileoverview provides a reusable Vimeo video UI component given a public
|
||||
* Vimeo video URL.
|
||||
*
|
||||
* goog.ui.media.Vimeo is actually a {@link goog.ui.ControlRenderer}, a
|
||||
* stateless class - that could/should be used as a Singleton with the static
|
||||
* method {@code goog.ui.media.Vimeo.getInstance} -, that knows how to render
|
||||
* video videos. It is designed to be used with a {@link goog.ui.Control},
|
||||
* which will actually control the media renderer and provide the
|
||||
* {@link goog.ui.Component} base. This design guarantees that all different
|
||||
* types of medias will behave alike but will look different.
|
||||
*
|
||||
* goog.ui.media.Vimeo expects vimeo video IDs on
|
||||
* {@code goog.ui.Control.getModel} as data models, and renders a flash object
|
||||
* that will show the contents of that video.
|
||||
*
|
||||
* Example of usage:
|
||||
*
|
||||
* <pre>
|
||||
* var video = goog.ui.media.VimeoModel.newInstance('http://vimeo.com/30012');
|
||||
* goog.ui.media.Vimeo.newControl(video).render();
|
||||
* </pre>
|
||||
*
|
||||
* Vimeo medias currently support the following states:
|
||||
*
|
||||
* <ul>
|
||||
* <li> {@link goog.ui.Component.State.DISABLED}: shows 'flash not available'
|
||||
* <li> {@link goog.ui.Component.State.HOVER}: mouse cursor is over the video
|
||||
* <li> {@link goog.ui.Component.State.SELECTED}: flash video is shown
|
||||
* </ul>
|
||||
*
|
||||
* Which can be accessed by
|
||||
* <pre>
|
||||
* video.setEnabled(true);
|
||||
* video.setHighlighted(true);
|
||||
* video.setSelected(true);
|
||||
* </pre>
|
||||
*
|
||||
*
|
||||
* @supported IE6, FF2+, Safari. Requires flash to actually work.
|
||||
*
|
||||
* TODO(user): test on other browsers
|
||||
*/
|
||||
|
||||
goog.provide('goog.ui.media.Vimeo');
|
||||
goog.provide('goog.ui.media.VimeoModel');
|
||||
|
||||
goog.require('goog.string');
|
||||
goog.require('goog.ui.media.FlashObject');
|
||||
goog.require('goog.ui.media.Media');
|
||||
goog.require('goog.ui.media.MediaModel');
|
||||
goog.require('goog.ui.media.MediaRenderer');
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Subclasses a goog.ui.media.MediaRenderer to provide a Vimeo specific media
|
||||
* renderer.
|
||||
*
|
||||
* This class knows how to parse Vimeo URLs, and render the DOM structure
|
||||
* of vimeo video players. This class is meant to be used as a singleton static
|
||||
* stateless class, that takes {@code goog.ui.media.Media} instances and renders
|
||||
* it. It expects {@code goog.ui.media.Media.getModel} to return a well formed,
|
||||
* previously constructed, vimeoId {@see goog.ui.media.Vimeo.parseUrl}, which is
|
||||
* the data model this renderer will use to construct the DOM structure.
|
||||
* {@see goog.ui.media.Vimeo.newControl} for a example of constructing a control
|
||||
* with this renderer.
|
||||
*
|
||||
* This design is patterned after http://go/closure_control_subclassing
|
||||
*
|
||||
* It uses {@link goog.ui.media.FlashObject} to embed the flash object.
|
||||
*
|
||||
* @constructor
|
||||
* @extends {goog.ui.media.MediaRenderer}
|
||||
* @final
|
||||
*/
|
||||
goog.ui.media.Vimeo = function() {
|
||||
goog.ui.media.MediaRenderer.call(this);
|
||||
};
|
||||
goog.inherits(goog.ui.media.Vimeo, goog.ui.media.MediaRenderer);
|
||||
goog.addSingletonGetter(goog.ui.media.Vimeo);
|
||||
|
||||
|
||||
/**
|
||||
* Default CSS class to be applied to the root element of components rendered
|
||||
* by this renderer.
|
||||
*
|
||||
* @type {string}
|
||||
*/
|
||||
goog.ui.media.Vimeo.CSS_CLASS = goog.getCssName('goog-ui-media-vimeo');
|
||||
|
||||
|
||||
/**
|
||||
* A static convenient method to construct a goog.ui.media.Media control out of
|
||||
* a Vimeo URL. It extracts the videoId information on the URL, sets it
|
||||
* as the data model goog.ui.media.Vimeo renderer uses, sets the states
|
||||
* supported by the renderer, and returns a Control that binds everything
|
||||
* together. This is what you should be using for constructing Vimeo videos,
|
||||
* except if you need more fine control over the configuration.
|
||||
*
|
||||
* @param {goog.ui.media.VimeoModel} dataModel A vimeo video URL.
|
||||
* @param {goog.dom.DomHelper=} opt_domHelper Optional DOM helper, used for
|
||||
* document interaction.
|
||||
* @return {!goog.ui.media.Media} A Control binded to the Vimeo renderer.
|
||||
*/
|
||||
goog.ui.media.Vimeo.newControl = function(dataModel, opt_domHelper) {
|
||||
var control = new goog.ui.media.Media(
|
||||
dataModel, goog.ui.media.Vimeo.getInstance(), opt_domHelper);
|
||||
// vimeo videos don't have any thumbnail for now, so we show the
|
||||
// "selected" version of the UI at the start, which is the
|
||||
// flash player.
|
||||
control.setSelected(true);
|
||||
return control;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Creates the initial DOM structure of the vimeo video, which is basically a
|
||||
* the flash object pointing to a vimeo video player.
|
||||
*
|
||||
* @param {goog.ui.Control} c The media control.
|
||||
* @return {!Element} The DOM structure that represents this control.
|
||||
* @override
|
||||
*/
|
||||
goog.ui.media.Vimeo.prototype.createDom = function(c) {
|
||||
var control = /** @type {goog.ui.media.Media} */ (c);
|
||||
var div = goog.ui.media.Vimeo.superClass_.createDom.call(this, control);
|
||||
|
||||
var dataModel =
|
||||
/** @type {goog.ui.media.VimeoModel} */ (control.getDataModel());
|
||||
|
||||
var flash = new goog.ui.media.FlashObject(
|
||||
dataModel.getPlayer().getTrustedResourceUrl(),
|
||||
control.getDomHelper());
|
||||
flash.render(div);
|
||||
|
||||
return div;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Returns the CSS class to be applied to the root element of components
|
||||
* rendered using this renderer.
|
||||
* @return {string} Renderer-specific CSS class.
|
||||
* @override
|
||||
*/
|
||||
goog.ui.media.Vimeo.prototype.getCssClass = function() {
|
||||
return goog.ui.media.Vimeo.CSS_CLASS;
|
||||
};
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* The {@code goog.ui.media.Vimeo} media data model. It stores a required
|
||||
* {@code videoId} field, sets the vimeo URL, and allows a few optional
|
||||
* parameters.
|
||||
*
|
||||
* @param {string} videoId The vimeo video id.
|
||||
* @param {string=} opt_caption An optional caption of the vimeo video.
|
||||
* @param {string=} opt_description An optional description of the vimeo video.
|
||||
* @param {boolean=} opt_autoplay Whether to autoplay video.
|
||||
* @constructor
|
||||
* @extends {goog.ui.media.MediaModel}
|
||||
* @final
|
||||
*/
|
||||
goog.ui.media.VimeoModel = function(videoId, opt_caption, opt_description,
|
||||
opt_autoplay) {
|
||||
goog.ui.media.MediaModel.call(
|
||||
this,
|
||||
goog.ui.media.VimeoModel.buildUrl(videoId),
|
||||
opt_caption,
|
||||
opt_description,
|
||||
goog.ui.media.MediaModel.MimeType.FLASH);
|
||||
|
||||
/**
|
||||
* The Vimeo video id.
|
||||
* @type {string}
|
||||
* @private
|
||||
*/
|
||||
this.videoId_ = videoId;
|
||||
|
||||
this.setPlayer(new goog.ui.media.MediaModel.Player(
|
||||
goog.ui.media.VimeoModel.buildFlashUrl(videoId, opt_autoplay)));
|
||||
};
|
||||
goog.inherits(goog.ui.media.VimeoModel, goog.ui.media.MediaModel);
|
||||
|
||||
|
||||
/**
|
||||
* Regular expression used to extract the vimeo video id out of vimeo URLs.
|
||||
*
|
||||
* Copied from http://go/markdownlite.js
|
||||
*
|
||||
* TODO(user): add support to https.
|
||||
*
|
||||
* @type {RegExp}
|
||||
* @private
|
||||
* @const
|
||||
*/
|
||||
goog.ui.media.VimeoModel.MATCHER_ =
|
||||
/https?:\/\/(?:www\.)?vimeo\.com\/(?:hd#)?([0-9]+)/i;
|
||||
|
||||
|
||||
/**
|
||||
* Takes a {@code vimeoUrl} and extracts the video id.
|
||||
*
|
||||
* @param {string} vimeoUrl A vimeo video URL.
|
||||
* @param {string=} opt_caption An optional caption of the vimeo video.
|
||||
* @param {string=} opt_description An optional description of the vimeo video.
|
||||
* @param {boolean=} opt_autoplay Whether to autoplay video.
|
||||
* @return {!goog.ui.media.VimeoModel} The vimeo data model that represents this
|
||||
* URL.
|
||||
* @throws exception in case the parsing fails
|
||||
*/
|
||||
goog.ui.media.VimeoModel.newInstance = function(vimeoUrl,
|
||||
opt_caption,
|
||||
opt_description,
|
||||
opt_autoplay) {
|
||||
if (goog.ui.media.VimeoModel.MATCHER_.test(vimeoUrl)) {
|
||||
var data = goog.ui.media.VimeoModel.MATCHER_.exec(vimeoUrl);
|
||||
return new goog.ui.media.VimeoModel(
|
||||
data[1], opt_caption, opt_description, opt_autoplay);
|
||||
}
|
||||
throw Error('failed to parse vimeo url: ' + vimeoUrl);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* The opposite of {@code goog.ui.media.Vimeo.parseUrl}: it takes a videoId
|
||||
* and returns a vimeo URL.
|
||||
*
|
||||
* @param {string} videoId The vimeo video ID.
|
||||
* @return {string} The vimeo URL.
|
||||
*/
|
||||
goog.ui.media.VimeoModel.buildUrl = function(videoId) {
|
||||
return 'http://vimeo.com/' + goog.string.urlEncode(videoId);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Builds a flash url from the vimeo {@code videoId}.
|
||||
*
|
||||
* @param {string} videoId The vimeo video ID.
|
||||
* @param {boolean=} opt_autoplay Whether the flash movie should start playing
|
||||
* as soon as it is shown, or if it should show a 'play' button.
|
||||
* @return {string} The vimeo flash URL.
|
||||
*/
|
||||
goog.ui.media.VimeoModel.buildFlashUrl = function(videoId, opt_autoplay) {
|
||||
var autoplay = opt_autoplay ? '&autoplay=1' : '';
|
||||
return 'http://vimeo.com/moogaloop.swf?clip_id=' +
|
||||
goog.string.urlEncode(videoId) +
|
||||
'&server=vimeo.com&show_title=1&show_byline=1&show_portrait=0color=&' +
|
||||
'fullscreen=1' + autoplay;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Gets the Vimeo video id.
|
||||
* @return {string} The Vimeo video id.
|
||||
*/
|
||||
goog.ui.media.VimeoModel.prototype.getVideoId = function() {
|
||||
return this.videoId_;
|
||||
};
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<!--
|
||||
Copyright 2009 The Closure Library Authors. All Rights Reserved.
|
||||
|
||||
Use of this source code is governed by the Apache License, Version 2.0.
|
||||
See the COPYING file for details.
|
||||
-->
|
||||
<!--
|
||||
-->
|
||||
<head>
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
||||
<title>
|
||||
Closure Unit Tests - goog.ui.media.Vimeo
|
||||
</title>
|
||||
<script src="../../base.js">
|
||||
</script>
|
||||
<script>
|
||||
goog.require('goog.ui.media.VimeoTest');
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,91 @@
|
||||
// Copyright 2009 The Closure Library Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS-IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
goog.provide('goog.ui.media.VimeoTest');
|
||||
goog.setTestOnly('goog.ui.media.VimeoTest');
|
||||
|
||||
goog.require('goog.dom');
|
||||
goog.require('goog.testing.jsunit');
|
||||
goog.require('goog.ui.media.FlashObject');
|
||||
goog.require('goog.ui.media.Media');
|
||||
goog.require('goog.ui.media.Vimeo');
|
||||
goog.require('goog.ui.media.VimeoModel');
|
||||
var vimeo;
|
||||
var control;
|
||||
var VIMEO_ID = '3001295';
|
||||
var VIMEO_URL = 'http://vimeo.com/' + VIMEO_ID;
|
||||
var VIMEO_URL_HD = 'http://vimeo.com/hd#' + VIMEO_ID;
|
||||
var VIMEO_URL_SECURE = 'https://vimeo.com/' + VIMEO_ID;
|
||||
var parent = goog.dom.createElement('div');
|
||||
|
||||
function setUp() {
|
||||
vimeo = new goog.ui.media.Vimeo();
|
||||
var model = new goog.ui.media.VimeoModel(VIMEO_ID, 'vimeo caption');
|
||||
control = new goog.ui.media.Media(model, vimeo);
|
||||
control.setSelected(true);
|
||||
}
|
||||
|
||||
function tearDown() {
|
||||
control.dispose();
|
||||
}
|
||||
|
||||
function testBasicRendering() {
|
||||
control.render(parent);
|
||||
var el = goog.dom.getElementsByTagNameAndClass(
|
||||
'div', goog.ui.media.Vimeo.CSS_CLASS, parent);
|
||||
assertEquals(1, el.length);
|
||||
assertEquals(VIMEO_URL, control.getDataModel().getUrl());
|
||||
}
|
||||
|
||||
function testParsingUrl() {
|
||||
assertExtractsCorrectly(VIMEO_ID, VIMEO_URL);
|
||||
assertExtractsCorrectly(VIMEO_ID, VIMEO_URL_HD);
|
||||
assertExtractsCorrectly(VIMEO_ID, VIMEO_URL_SECURE);
|
||||
|
||||
var invalidUrl = 'http://invalidUrl/filename.doc';
|
||||
var e = assertThrows('parser expects a well formed URL', function() {
|
||||
goog.ui.media.VimeoModel.newInstance(invalidUrl);
|
||||
});
|
||||
assertEquals('failed to parse vimeo url: ' + invalidUrl, e.message);
|
||||
}
|
||||
|
||||
function testBuildingUrl() {
|
||||
assertEquals(
|
||||
VIMEO_URL, goog.ui.media.VimeoModel.buildUrl(VIMEO_ID));
|
||||
}
|
||||
|
||||
function testCreatingModel() {
|
||||
var model = new goog.ui.media.VimeoModel(VIMEO_ID);
|
||||
assertEquals(VIMEO_ID, model.getVideoId());
|
||||
assertEquals(VIMEO_URL, model.getUrl());
|
||||
assertUndefined(model.getCaption());
|
||||
}
|
||||
|
||||
function testCreatingDomOnInitialState() {
|
||||
control.render(parent);
|
||||
var caption = goog.dom.getElementsByTagNameAndClass(
|
||||
'div',
|
||||
goog.ui.media.Vimeo.CSS_CLASS + '-caption',
|
||||
parent);
|
||||
assertEquals(1, caption.length);
|
||||
|
||||
var flash = goog.dom.getElementsByTagNameAndClass(
|
||||
'div', goog.ui.media.FlashObject.CSS_CLASS, parent);
|
||||
assertEquals(1, flash.length);
|
||||
}
|
||||
|
||||
function assertExtractsCorrectly(expectedVideoId, url) {
|
||||
var model = goog.ui.media.VimeoModel.newInstance(url);
|
||||
assertEquals('Video id for ' + url, expectedVideoId, model.getVideoId());
|
||||
}
|
||||
@@ -0,0 +1,358 @@
|
||||
// Copyright 2009 The Closure Library Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS-IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
/**
|
||||
* @fileoverview provides a reusable youtube UI component given a youtube data
|
||||
* model.
|
||||
*
|
||||
* goog.ui.media.Youtube is actually a {@link goog.ui.ControlRenderer}, a
|
||||
* stateless class - that could/should be used as a Singleton with the static
|
||||
* method {@code goog.ui.media.Youtube.getInstance} -, that knows how to render
|
||||
* youtube videos. It is designed to be used with a {@link goog.ui.Control},
|
||||
* which will actually control the media renderer and provide the
|
||||
* {@link goog.ui.Component} base. This design guarantees that all different
|
||||
* types of medias will behave alike but will look different.
|
||||
*
|
||||
* goog.ui.media.Youtube expects {@code goog.ui.media.YoutubeModel} on
|
||||
* {@code goog.ui.Control.getModel} as data models, and render a flash object
|
||||
* that will play that URL.
|
||||
*
|
||||
* Example of usage:
|
||||
*
|
||||
* <pre>
|
||||
* var video = goog.ui.media.YoutubeModel.newInstance(
|
||||
* 'http://www.youtube.com/watch?v=ddl5f44spwQ');
|
||||
* goog.ui.media.Youtube.newControl(video).render();
|
||||
* </pre>
|
||||
*
|
||||
* youtube medias currently support the following states:
|
||||
*
|
||||
* <ul>
|
||||
* <li> {@link goog.ui.Component.State.DISABLED}: shows 'flash not available'
|
||||
* <li> {@link goog.ui.Component.State.HOVER}: mouse cursor is over the video
|
||||
* <li> {@link !goog.ui.Component.State.SELECTED}: a static thumbnail is shown
|
||||
* <li> {@link goog.ui.Component.State.SELECTED}: video is playing
|
||||
* </ul>
|
||||
*
|
||||
* Which can be accessed by
|
||||
* <pre>
|
||||
* youtube.setEnabled(true);
|
||||
* youtube.setHighlighted(true);
|
||||
* youtube.setSelected(true);
|
||||
* </pre>
|
||||
*
|
||||
* This package also provides a few static auxiliary methods, such as:
|
||||
*
|
||||
* <pre>
|
||||
* var videoId = goog.ui.media.Youtube.parseUrl(
|
||||
* 'http://www.youtube.com/watch?v=ddl5f44spwQ');
|
||||
* </pre>
|
||||
*
|
||||
*
|
||||
* @supported IE6, FF2+, Safari. Requires flash to actually work.
|
||||
*
|
||||
* TODO(user): test on other browsers
|
||||
*/
|
||||
|
||||
|
||||
goog.provide('goog.ui.media.Youtube');
|
||||
goog.provide('goog.ui.media.YoutubeModel');
|
||||
|
||||
goog.require('goog.string');
|
||||
goog.require('goog.ui.Component');
|
||||
goog.require('goog.ui.media.FlashObject');
|
||||
goog.require('goog.ui.media.Media');
|
||||
goog.require('goog.ui.media.MediaModel');
|
||||
goog.require('goog.ui.media.MediaRenderer');
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Subclasses a goog.ui.media.MediaRenderer to provide a Youtube specific media
|
||||
* renderer.
|
||||
*
|
||||
* This class knows how to parse youtube urls, and render the DOM structure
|
||||
* of youtube video players and previews. This class is meant to be used as a
|
||||
* singleton static stateless class, that takes {@code goog.ui.media.Media}
|
||||
* instances and renders it. It expects {@code goog.ui.media.Media.getModel} to
|
||||
* return a well formed, previously constructed, youtube video id, which is the
|
||||
* data model this renderer will use to construct the DOM structure.
|
||||
* {@see goog.ui.media.Youtube.newControl} for a example of constructing a
|
||||
* control with this renderer.
|
||||
*
|
||||
* goog.ui.media.Youtube currently supports all {@link goog.ui.Component.State}.
|
||||
* It will change its DOM structure between SELECTED and !SELECTED, and rely on
|
||||
* CSS definitions on the others. On !SELECTED, the renderer will render a
|
||||
* youtube static <img>, with a thumbnail of the video. On SELECTED, the
|
||||
* renderer will append to the DOM a flash object, that contains the youtube
|
||||
* video.
|
||||
*
|
||||
* This design is patterned after http://go/closure_control_subclassing
|
||||
*
|
||||
* It uses {@link goog.ui.media.FlashObject} to embed the flash object.
|
||||
*
|
||||
* @constructor
|
||||
* @extends {goog.ui.media.MediaRenderer}
|
||||
* @final
|
||||
*/
|
||||
goog.ui.media.Youtube = function() {
|
||||
goog.ui.media.MediaRenderer.call(this);
|
||||
};
|
||||
goog.inherits(goog.ui.media.Youtube, goog.ui.media.MediaRenderer);
|
||||
goog.addSingletonGetter(goog.ui.media.Youtube);
|
||||
|
||||
|
||||
/**
|
||||
* A static convenient method to construct a goog.ui.media.Media control out of
|
||||
* a youtube model. It sets it as the data model goog.ui.media.Youtube renderer
|
||||
* uses, sets the states supported by the renderer, and returns a Control that
|
||||
* binds everything together. This is what you should be using for constructing
|
||||
* Youtube videos, except if you need finer control over the configuration.
|
||||
*
|
||||
* @param {goog.ui.media.YoutubeModel} youtubeModel The youtube data model.
|
||||
* @param {goog.dom.DomHelper=} opt_domHelper Optional DOM helper, used for
|
||||
* document interaction.
|
||||
* @return {!goog.ui.media.Media} A Control binded to the youtube renderer.
|
||||
*/
|
||||
goog.ui.media.Youtube.newControl = function(youtubeModel, opt_domHelper) {
|
||||
var control = new goog.ui.media.Media(
|
||||
youtubeModel,
|
||||
goog.ui.media.Youtube.getInstance(),
|
||||
opt_domHelper);
|
||||
control.setStateInternal(goog.ui.Component.State.ACTIVE);
|
||||
return control;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Default CSS class to be applied to the root element of components rendered
|
||||
* by this renderer.
|
||||
* @type {string}
|
||||
*/
|
||||
goog.ui.media.Youtube.CSS_CLASS = goog.getCssName('goog-ui-media-youtube');
|
||||
|
||||
|
||||
/**
|
||||
* Changes the state of a {@code control}. Currently only changes the DOM
|
||||
* structure when the youtube movie is SELECTED (by default fired by a MOUSEUP
|
||||
* on the thumbnail), which means we have to embed the youtube flash video and
|
||||
* play it.
|
||||
*
|
||||
* @param {goog.ui.Control} c The media control.
|
||||
* @param {goog.ui.Component.State} state The state to be set or cleared.
|
||||
* @param {boolean} enable Whether the state is enabled or disabled.
|
||||
* @override
|
||||
*/
|
||||
goog.ui.media.Youtube.prototype.setState = function(c, state, enable) {
|
||||
var control = /** @type {goog.ui.media.Media} */ (c);
|
||||
goog.ui.media.Youtube.superClass_.setState.call(this, control, state, enable);
|
||||
|
||||
// control.createDom has to be called before any state is set.
|
||||
// Use control.setStateInternal if you need to set states
|
||||
if (!control.getElement()) {
|
||||
throw Error(goog.ui.Component.Error.STATE_INVALID);
|
||||
}
|
||||
|
||||
var domHelper = control.getDomHelper();
|
||||
var dataModel =
|
||||
/** @type {goog.ui.media.YoutubeModel} */ (control.getDataModel());
|
||||
|
||||
if (!!(state & goog.ui.Component.State.SELECTED) && enable) {
|
||||
var flashEls = domHelper.getElementsByTagNameAndClass(
|
||||
'div',
|
||||
goog.ui.media.FlashObject.CSS_CLASS,
|
||||
control.getElement());
|
||||
if (flashEls.length > 0) {
|
||||
return;
|
||||
}
|
||||
var youtubeFlash = new goog.ui.media.FlashObject(
|
||||
dataModel.getPlayer().getTrustedResourceUrl(),
|
||||
domHelper);
|
||||
control.addChild(youtubeFlash, true);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Returns the CSS class to be applied to the root element of components
|
||||
* rendered using this renderer.
|
||||
*
|
||||
* @return {string} Renderer-specific CSS class.
|
||||
* @override
|
||||
*/
|
||||
goog.ui.media.Youtube.prototype.getCssClass = function() {
|
||||
return goog.ui.media.Youtube.CSS_CLASS;
|
||||
};
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* The {@code goog.ui.media.Youtube} media data model. It stores a required
|
||||
* {@code videoId} field, sets the youtube URL, and allows a few optional
|
||||
* parameters.
|
||||
*
|
||||
* @param {string} videoId The youtube video id.
|
||||
* @param {string=} opt_caption An optional caption of the youtube video.
|
||||
* @param {string=} opt_description An optional description of the youtube
|
||||
* video.
|
||||
* @constructor
|
||||
* @extends {goog.ui.media.MediaModel}
|
||||
* @final
|
||||
*/
|
||||
goog.ui.media.YoutubeModel = function(videoId, opt_caption, opt_description) {
|
||||
goog.ui.media.MediaModel.call(
|
||||
this,
|
||||
goog.ui.media.YoutubeModel.buildUrl(videoId),
|
||||
opt_caption,
|
||||
opt_description,
|
||||
goog.ui.media.MediaModel.MimeType.FLASH);
|
||||
|
||||
/**
|
||||
* The Youtube video id.
|
||||
* @type {string}
|
||||
* @private
|
||||
*/
|
||||
this.videoId_ = videoId;
|
||||
|
||||
this.setThumbnails([new goog.ui.media.MediaModel.Thumbnail(
|
||||
goog.ui.media.YoutubeModel.getThumbnailUrl(videoId))]);
|
||||
|
||||
this.setPlayer(new goog.ui.media.MediaModel.Player(
|
||||
goog.ui.media.YoutubeModel.getFlashUrl(videoId, true)));
|
||||
};
|
||||
goog.inherits(goog.ui.media.YoutubeModel, goog.ui.media.MediaModel);
|
||||
|
||||
|
||||
/**
|
||||
* A youtube regular expression matcher. It matches the VIDEOID of URLs like
|
||||
* http://www.youtube.com/watch?v=VIDEOID. Based on:
|
||||
* googledata/contentonebox/opencob/specs/common/YTPublicExtractorCard.xml
|
||||
* @type {RegExp}
|
||||
* @private
|
||||
* @const
|
||||
*/
|
||||
// Be careful about the placement of the dashes in the character classes. Eg,
|
||||
// use "[\\w=-]" instead of "[\\w-=]" if you mean to include the dash as a
|
||||
// character and not create a character range like "[a-f]".
|
||||
goog.ui.media.YoutubeModel.MATCHER_ = new RegExp(
|
||||
// Lead in.
|
||||
'https?://(?:[a-zA-Z]{1,3}\\.)?' +
|
||||
// Watch URL prefix. This should handle new URLs of the form:
|
||||
// http://www.youtube.com/watch#!v=jqxENMKaeCU&feature=related
|
||||
// where the parameters appear after "#!" instead of "?".
|
||||
'(?:youtube\\.com/watch|youtu\\.be/watch)' +
|
||||
// Get the video id:
|
||||
// The video ID is a parameter v=[videoid] either right after the "?"
|
||||
// or after some other parameters.
|
||||
'(?:\\?(?:[\\w=-]+&(?:amp;)?)*v=([\\w-]+)' +
|
||||
'(?:&(?:amp;)?[\\w=-]+)*)?' +
|
||||
// Get any extra arguments in the URL's hash part.
|
||||
'(?:#[!]?(?:' +
|
||||
// Video ID from the v=[videoid] parameter, optionally surrounded by other
|
||||
// & separated parameters.
|
||||
'(?:(?:[\\w=-]+&(?:amp;)?)*(?:v=([\\w-]+))' +
|
||||
'(?:&(?:amp;)?[\\w=-]+)*)' +
|
||||
'|' +
|
||||
// Continue supporting "?" for the video ID
|
||||
// and "#" for other hash parameters.
|
||||
'(?:[\\w=&-]+)' +
|
||||
'))?' +
|
||||
// Should terminate with a non-word, non-dash (-) character.
|
||||
'[^\\w-]?', 'i');
|
||||
|
||||
|
||||
/**
|
||||
* A auxiliary static method that parses a youtube URL, extracting the ID of the
|
||||
* video, and builds a YoutubeModel.
|
||||
*
|
||||
* @param {string} youtubeUrl A youtube URL.
|
||||
* @param {string=} opt_caption An optional caption of the youtube video.
|
||||
* @param {string=} opt_description An optional description of the youtube
|
||||
* video.
|
||||
* @return {!goog.ui.media.YoutubeModel} The data model that represents the
|
||||
* youtube URL.
|
||||
* @see goog.ui.media.YoutubeModel.getVideoId()
|
||||
* @throws Error in case the parsing fails.
|
||||
*/
|
||||
goog.ui.media.YoutubeModel.newInstance = function(youtubeUrl,
|
||||
opt_caption,
|
||||
opt_description) {
|
||||
var extract = goog.ui.media.YoutubeModel.MATCHER_.exec(youtubeUrl);
|
||||
if (extract) {
|
||||
var videoId = extract[1] || extract[2];
|
||||
return new goog.ui.media.YoutubeModel(
|
||||
videoId, opt_caption, opt_description);
|
||||
}
|
||||
|
||||
throw Error('failed to parse video id from youtube url: ' + youtubeUrl);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* The opposite of {@code goog.ui.media.Youtube.newInstance}: it takes a videoId
|
||||
* and returns a youtube URL.
|
||||
*
|
||||
* @param {string} videoId The youtube video ID.
|
||||
* @return {string} The youtube URL.
|
||||
*/
|
||||
goog.ui.media.YoutubeModel.buildUrl = function(videoId) {
|
||||
return 'http://www.youtube.com/watch?v=' + goog.string.urlEncode(videoId);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* A static auxiliary method that builds a static image URL with a preview of
|
||||
* the youtube video.
|
||||
*
|
||||
* NOTE(user): patterned after Gmail's gadgets/youtube,
|
||||
*
|
||||
* TODO(user): how do I specify the width/height of the resulting image on the
|
||||
* url ? is there an official API for http://ytimg.com ?
|
||||
*
|
||||
* @param {string} youtubeId The youtube video ID.
|
||||
* @return {string} An URL that contains an image with a preview of the youtube
|
||||
* movie.
|
||||
*/
|
||||
goog.ui.media.YoutubeModel.getThumbnailUrl = function(youtubeId) {
|
||||
return 'http://i.ytimg.com/vi/' + youtubeId + '/default.jpg';
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* A static auxiliary method that builds URL of the flash movie to be embedded,
|
||||
* out of the youtube video id.
|
||||
*
|
||||
* @param {string} videoId The youtube video ID.
|
||||
* @param {boolean=} opt_autoplay Whether the flash movie should start playing
|
||||
* as soon as it is shown, or if it should show a 'play' button.
|
||||
* @return {string} The flash URL to be embedded on the page.
|
||||
*/
|
||||
goog.ui.media.YoutubeModel.getFlashUrl = function(videoId, opt_autoplay) {
|
||||
var autoplay = opt_autoplay ? '&autoplay=1' : '';
|
||||
// YouTube video ids are extracted from youtube URLs, which are user
|
||||
// generated input. the video id is later used to embed a flash object,
|
||||
// which is generated through HTML construction. We goog.string.urlEncode
|
||||
// the video id to make sure the URL is safe to be embedded.
|
||||
return 'http://www.youtube.com/v/' + goog.string.urlEncode(videoId) +
|
||||
'&hl=en&fs=1' + autoplay;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Gets the Youtube video id.
|
||||
* @return {string} The Youtube video id.
|
||||
*/
|
||||
goog.ui.media.YoutubeModel.prototype.getVideoId = function() {
|
||||
return this.videoId_;
|
||||
};
|
||||
@@ -0,0 +1,24 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<!--
|
||||
Copyright 2009 The Closure Library Authors. All Rights Reserved.
|
||||
|
||||
Use of this source code is governed by the Apache License, Version 2.0.
|
||||
See the COPYING file for details.
|
||||
-->
|
||||
<!--
|
||||
-->
|
||||
<head>
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
||||
<title>
|
||||
Closure Unit Tests - goog.ui.media.Youtube
|
||||
</title>
|
||||
<script src="../../base.js">
|
||||
</script>
|
||||
<script>
|
||||
goog.require('goog.ui.media.YoutubeTest');
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,259 @@
|
||||
// Copyright 2009 The Closure Library Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS-IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
goog.provide('goog.ui.media.YoutubeTest');
|
||||
goog.setTestOnly('goog.ui.media.YoutubeTest');
|
||||
|
||||
goog.require('goog.dom');
|
||||
goog.require('goog.testing.jsunit');
|
||||
goog.require('goog.ui.media.FlashObject');
|
||||
goog.require('goog.ui.media.Youtube');
|
||||
goog.require('goog.ui.media.YoutubeModel');
|
||||
var youtube;
|
||||
var control;
|
||||
var YOUTUBE_VIDEO_ID = 'dMH0bHeiRNg';
|
||||
var YOUTUBE_URL = 'http://www.youtube.com/watch?v=' + YOUTUBE_VIDEO_ID;
|
||||
var parent = goog.dom.createElement('div');
|
||||
|
||||
function setUp() {
|
||||
var model = new goog.ui.media.YoutubeModel(
|
||||
YOUTUBE_VIDEO_ID, 'evolution of dance');
|
||||
control = goog.ui.media.Youtube.newControl(model);
|
||||
}
|
||||
|
||||
function tearDown() {
|
||||
control.dispose();
|
||||
}
|
||||
|
||||
function testBasicRendering() {
|
||||
control.render(parent);
|
||||
var el = goog.dom.getElementsByTagNameAndClass('div',
|
||||
goog.ui.media.Youtube.CSS_CLASS, parent);
|
||||
assertEquals(1, el.length);
|
||||
assertEquals(YOUTUBE_URL, control.getDataModel().getUrl());
|
||||
}
|
||||
|
||||
function testParsingUrl() {
|
||||
// a simple link
|
||||
assertExtractsCorrectly(
|
||||
'uddeBVmKTqE',
|
||||
'http://www.youtube.com/watch?v=uddeBVmKTqE');
|
||||
// a simple mobile link
|
||||
assertExtractsCorrectly(
|
||||
'uddeBVmKTqE',
|
||||
'http://m.youtube.com/watch?v=uddeBVmKTqE');
|
||||
// a secure mobile link
|
||||
assertExtractsCorrectly(
|
||||
'uddeBVmKTqE',
|
||||
'https://m.youtube.com/watch?v=uddeBVmKTqE');
|
||||
// a simple short link
|
||||
assertExtractsCorrectly(
|
||||
'uddeBVmKTqE',
|
||||
'http://youtu.be/watch?v=uddeBVmKTqE');
|
||||
// a secure short link
|
||||
assertExtractsCorrectly(
|
||||
'uddeBVmKTqE',
|
||||
'https://youtu.be/watch?v=uddeBVmKTqE');
|
||||
// a channel link
|
||||
assertExtractsCorrectly(
|
||||
'4Pb9e1uu3EQ',
|
||||
'http://www.youtube.com/watch?v=4Pb9e1uu3EQ&feature=channel');
|
||||
// a UK link
|
||||
assertExtractsCorrectly(
|
||||
'xqWXO87TlH4',
|
||||
'http://uk.youtube.com/watch?gl=GB&hl=en-GB&v=xqWXO87TlH4');
|
||||
// an India link
|
||||
assertExtractsCorrectly(
|
||||
'10FKWOn4qGA',
|
||||
'http://www.youtube.com/watch?gl=IN&hl=en-GB&v=10FKWOn4qGA');
|
||||
// an ad
|
||||
assertExtractsCorrectly(
|
||||
'wk1_kDJhyBk',
|
||||
'http://www.youtube.com/watch?v=wk1_kDJhyBk&feature=yva-video-display');
|
||||
// a related video
|
||||
assertExtractsCorrectly(
|
||||
'7qL2PuLF0SI',
|
||||
'http://www.youtube.com/watch?v=7qL2PuLF0SI&feature=related');
|
||||
// with a timestamp
|
||||
assertExtractsCorrectly(
|
||||
'siJZXtsdfsf',
|
||||
'http://www.youtube.com/watch?v=siJZXtsdfsf#t=2m59s');
|
||||
// with a timestamp and multiple hash params
|
||||
assertExtractsCorrectly(
|
||||
'siJZXtabdef',
|
||||
'http://www.youtube.com/watch?v=siJZXtabdef#t=1m59s&videos=foo');
|
||||
// with a timestamp, multiple regular and hash params
|
||||
assertExtractsCorrectly(
|
||||
'siJZXtabxyz',
|
||||
'http://www.youtube.com/watch?foo=bar&v=siJZXtabxyz&x=y#t=1m30s' +
|
||||
'&videos=bar');
|
||||
// only hash params
|
||||
assertExtractsCorrectly(
|
||||
'MWBpQoPwT3U',
|
||||
'http://www.youtube.com/watch#!playnext=1&playnext_from=TL' +
|
||||
'&videos=RX1XPmgerGo&v=MWBpQoPwT3U');
|
||||
// only hash params
|
||||
assertExtractsCorrectly(
|
||||
'MWBpQoPwT3V',
|
||||
'http://www.youtube.com/watch#!playnext=1&playnext_from=TL' +
|
||||
'&videos=RX1XPmgerGp&v=MWBpQoPwT3V&foo=bar');
|
||||
assertExtractsCorrectly(
|
||||
'jqxENMKaeCU',
|
||||
'http://www.youtube.com/watch#!v=jqxENMKaeCU&feature=related');
|
||||
// Lots of query params, some of them w/ numbers, one of them before the
|
||||
// video ID
|
||||
assertExtractsCorrectly(
|
||||
'qbce2yN81mE',
|
||||
'http://www.youtube.com/watch?usg=AFQjCNFf90T3fekgdVBmPp-Wgya5_CTSaw' +
|
||||
'&v=qbce2yN81mE&source=video&vgc=rss');
|
||||
assertExtractsCorrectly(
|
||||
'Lc-8onVA5Jk',
|
||||
'http://www.youtube.com/watch?v=Lc-8onVA5Jk&feature=dir');
|
||||
// Last character in the video ID is '-' (a non-word but valid character)
|
||||
// and the video ID is the last query parameter
|
||||
assertExtractsCorrectly(
|
||||
'Lc-8onV5Jk-',
|
||||
'http://www.youtube.com/watch?v=Lc-8onV5Jk-');
|
||||
|
||||
var invalidUrls = [
|
||||
'http://invalidUrl/watch?v=dMH0bHeiRNg',
|
||||
'http://www$youtube.com/watch?v=dMH0bHeiRNg',
|
||||
'http://www.youtube$com/watch?v=dMH0bHeiRNg',
|
||||
'http://w_w.youtube.com/watch?v=dMH0bHeiRNg'
|
||||
];
|
||||
for (var i = 0, j = invalidUrls.length; i < j; ++i) {
|
||||
var e = assertThrows('parser expects a well formed URL', function() {
|
||||
goog.ui.media.YoutubeModel.newInstance(invalidUrls[i]);
|
||||
});
|
||||
assertEquals(
|
||||
'failed to parse video id from youtube url: ' + invalidUrls[i],
|
||||
e.message);
|
||||
}
|
||||
}
|
||||
|
||||
function testBuildingUrl() {
|
||||
assertEquals(
|
||||
YOUTUBE_URL, goog.ui.media.YoutubeModel.buildUrl(YOUTUBE_VIDEO_ID));
|
||||
}
|
||||
|
||||
function testCreatingModel() {
|
||||
var model = new goog.ui.media.YoutubeModel(YOUTUBE_VIDEO_ID);
|
||||
assertEquals(YOUTUBE_VIDEO_ID, model.getVideoId());
|
||||
assertEquals(YOUTUBE_URL, model.getUrl());
|
||||
assertUndefined(model.getCaption());
|
||||
}
|
||||
|
||||
function testCreatingDomOnInitialState() {
|
||||
control.render(parent);
|
||||
var preview = goog.dom.getElementsByTagNameAndClass(
|
||||
'img',
|
||||
goog.ui.media.Youtube.CSS_CLASS + '-thumbnail0',
|
||||
parent);
|
||||
assertEquals(1, preview.length);
|
||||
|
||||
var caption = goog.dom.getElementsByTagNameAndClass(
|
||||
'div',
|
||||
goog.ui.media.Youtube.CSS_CLASS + '-caption',
|
||||
parent);
|
||||
assertEquals(1, caption.length);
|
||||
|
||||
var flash = goog.dom.getElementsByTagNameAndClass('div',
|
||||
goog.ui.media.FlashObject.CSS_CLASS);
|
||||
assertEquals(0, flash.length);
|
||||
}
|
||||
|
||||
function testCreatingDomOnSelectedState() {
|
||||
control.render(parent);
|
||||
control.setSelected(true);
|
||||
var preview = goog.dom.getElementsByTagNameAndClass(
|
||||
'img',
|
||||
goog.ui.media.Youtube.CSS_CLASS + '-preview',
|
||||
parent);
|
||||
assertEquals(0, preview.length);
|
||||
|
||||
var caption = goog.dom.getElementsByTagNameAndClass(
|
||||
'div',
|
||||
goog.ui.media.Youtube.CSS_CLASS + '-caption',
|
||||
parent);
|
||||
assertEquals(1, caption.length);
|
||||
|
||||
var flash = goog.dom.getElementsByTagNameAndClass('div',
|
||||
goog.ui.media.FlashObject.CSS_CLASS, parent);
|
||||
assertEquals(1, flash.length);
|
||||
}
|
||||
|
||||
function testSettingSelectedStateAfterRender() {
|
||||
control.render(parent);
|
||||
control.setSelected(true);
|
||||
|
||||
var preview = goog.dom.getElementsByTagNameAndClass(
|
||||
'img',
|
||||
goog.ui.media.Youtube.CSS_CLASS + '-preview',
|
||||
parent);
|
||||
assertEquals(0, preview.length);
|
||||
|
||||
var caption = goog.dom.getElementsByTagNameAndClass(
|
||||
'div',
|
||||
goog.ui.media.Youtube.CSS_CLASS + '-caption',
|
||||
parent);
|
||||
assertEquals(1, caption.length);
|
||||
|
||||
var flash = goog.dom.getElementsByTagNameAndClass('div',
|
||||
goog.ui.media.FlashObject.CSS_CLASS, parent);
|
||||
assertEquals(1, flash.length);
|
||||
|
||||
control.setSelected(false);
|
||||
|
||||
var preview = goog.dom.getElementsByTagNameAndClass(
|
||||
'img',
|
||||
goog.ui.media.Youtube.CSS_CLASS + '-thumbnail0',
|
||||
parent);
|
||||
assertEquals(1, preview.length);
|
||||
|
||||
var caption = goog.dom.getElementsByTagNameAndClass(
|
||||
'div',
|
||||
goog.ui.media.Youtube.CSS_CLASS + '-caption',
|
||||
parent);
|
||||
assertEquals(1, caption.length);
|
||||
|
||||
// setting select as false doesn't actually remove the flash movie from
|
||||
// the DOM tree, which means that setting selected to true won't actually
|
||||
// restart the movie. TODO(user): fix this.
|
||||
var flash = goog.dom.getElementsByTagNameAndClass('div',
|
||||
goog.ui.media.FlashObject.CSS_CLASS, parent);
|
||||
assertEquals(1, flash.length);
|
||||
|
||||
control.setSelected(true);
|
||||
|
||||
var flash = goog.dom.getElementsByTagNameAndClass('div',
|
||||
goog.ui.media.FlashObject.CSS_CLASS, parent);
|
||||
assertEquals(1, flash.length);
|
||||
}
|
||||
|
||||
function testUrlMatcher() {
|
||||
var matcher = goog.ui.media.YoutubeModel.MATCHER_;
|
||||
assertTrue(matcher.test('http://www.youtube.com/watch?v=55D-ybnYQSs'));
|
||||
assertTrue(matcher.test('https://youtube.com/watch?v=55D-ybnYQSs'));
|
||||
assertTrue(
|
||||
matcher.test('https://youtube.com/watch?blarg=blop&v=55D-ybnYQSs'));
|
||||
assertTrue(matcher.test('http://www.youtube.com/watch?v=55D-ybnYQSs#wee'));
|
||||
|
||||
assertFalse(matcher.test('http://www.cnn.com/watch?v=55D-ybnYQSs#wee'));
|
||||
assertFalse(matcher.test('ftp://www.youtube.com/watch?v=55D-ybnYQSs#wee'));
|
||||
}
|
||||
|
||||
function assertExtractsCorrectly(expectedVideoId, url) {
|
||||
var youtube = goog.ui.media.YoutubeModel.newInstance(url);
|
||||
assertEquals('videoid for ' + url, expectedVideoId, youtube.getVideoId());
|
||||
}
|
||||
Reference in New Issue
Block a user