| // 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'); |
| } |
| }; |