media.js 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298
  1. // Copyright 2009 The Closure Library Authors. All Rights Reserved.
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS-IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. /**
  15. * @fileoverview Provides the base goog.ui.Control and goog.ui.ControlRenderer
  16. * for media types, as well as a media model consistent with the Yahoo Media RSS
  17. * specification {@link http://search.yahoo.com/mrss/}.
  18. *
  19. * The goog.ui.media.* package is basically a set of goog.ui.ControlRenderers
  20. * subclasses (such as goog.ui.media.Youtube, goog.ui.media.Picasa, etc) that
  21. * should all work with the same goog.ui.Control (goog.ui.media.Media) logic.
  22. *
  23. * This design guarantees that all different types of medias will behave alike
  24. * (in a base level) but will look different.
  25. *
  26. * In MVC terms, {@link goog.ui.media.Media} is the Controller,
  27. * {@link goog.ui.media.MediaRenderer} + CSS definitions are the View and
  28. * {@code goog.ui.media.MediaModel} is the data Model. Typically,
  29. * MediaRenderer will be subclassed to provide media specific renderers.
  30. * MediaRenderer subclasses are also responsible for defining the data model.
  31. *
  32. * This design is strongly patterned after:
  33. * http://go/closure_control_subclassing
  34. *
  35. * goog.ui.media.MediaRenderer handles the basic common ways to display media,
  36. * such as displaying tooltips, frames, minimize/maximize buttons, play buttons,
  37. * etc. Its subclasses are responsible for rendering media specific DOM
  38. * structures, like youtube flash players, picasa albums, etc.
  39. *
  40. * goog.ui.media.Media handles the Control of Medias, by listening to events
  41. * and firing the appropriate actions. It knows about the existence of captions,
  42. * minimize/maximize buttons, and takes all the actions needed to change states,
  43. * including delegating the UI actions to MediaRenderers.
  44. *
  45. * Although MediaRenderer is a base class designed to be subclassed, it can
  46. * be used by itself:
  47. *
  48. * <pre>
  49. * var renderer = new goog.ui.media.MediaRenderer();
  50. * var control = new goog.ui.media.Media('hello world', renderer);
  51. * var control.render(goog.dom.getElement('mediaHolder'));
  52. * </pre>
  53. *
  54. * It requires a few CSS rules to be defined, which you should use to control
  55. * how the component is displayed. {@link goog.ui.ControlRenderer}s is very CSS
  56. * intensive, which separates the UI structure (the HTML DOM elements, which is
  57. * created by the {@code goog.ui.media.MediaRenderer}) from the UI view (which
  58. * nodes are visible, which aren't, where they are positioned. These are defined
  59. * on the CSS rules for each state). A few examples of CSS selectors that needs
  60. * to be defined are:
  61. *
  62. * <ul>
  63. * <li>.goog-ui-media
  64. * <li>.goog-ui-media-hover
  65. * <li>.goog-ui-media-selected
  66. * </ul>
  67. *
  68. * If you want to have different custom renderers CSS namespaces (eg. you may
  69. * want to show a small thumbnail, or you may want to hide the caption, etc),
  70. * you can do so by using:
  71. *
  72. * <pre>
  73. * var renderer = goog.ui.ControlRenderer.getCustomRenderer(
  74. * goog.ui.media.MediaRenderer, 'my-custom-namespace');
  75. * var media = new goog.ui.media.Media('', renderer);
  76. * media.render(goog.dom.getElement('parent'));
  77. * </pre>
  78. *
  79. * Which will allow you to set your own .my-custom-namespace-hover,
  80. * .my-custom-namespace-selected CSS selectors.
  81. *
  82. * NOTE(user): it seems like an overkill to subclass goog.ui.Control instead of
  83. * using a factory, but we wanted to make sure we had more control over the
  84. * events for future media implementations. Since we intent to use it in many
  85. * different places, it makes sense to have a more flexible design that lets us
  86. * control the inner workings of goog.ui.Control.
  87. *
  88. * TODO(user): implement, as needed, the Media specific state changes UI, such
  89. * as minimize/maximize buttons, expand/close buttons, etc.
  90. *
  91. */
  92. goog.provide('goog.ui.media.Media');
  93. goog.provide('goog.ui.media.MediaRenderer');
  94. goog.require('goog.asserts');
  95. goog.require('goog.dom.TagName');
  96. goog.require('goog.style');
  97. goog.require('goog.ui.Component');
  98. goog.require('goog.ui.Control');
  99. goog.require('goog.ui.ControlRenderer');
  100. /**
  101. * Provides the control mechanism of media types.
  102. *
  103. * @param {goog.ui.media.MediaModel} dataModel The data model to be used by the
  104. * renderer.
  105. * @param {goog.ui.ControlRenderer=} opt_renderer Renderer used to render or
  106. * decorate the component; defaults to {@link goog.ui.ControlRenderer}.
  107. * @param {goog.dom.DomHelper=} opt_domHelper Optional DOM helper, used for
  108. * document interaction.
  109. * @constructor
  110. * @extends {goog.ui.Control}
  111. * @final
  112. */
  113. goog.ui.media.Media = function(dataModel, opt_renderer, opt_domHelper) {
  114. goog.ui.Control.call(this, null, opt_renderer, opt_domHelper);
  115. // Sets up the data model.
  116. this.setDataModel(dataModel);
  117. this.setSupportedState(goog.ui.Component.State.OPENED, true);
  118. this.setSupportedState(goog.ui.Component.State.SELECTED, true);
  119. // TODO(user): had to do this to for mouseDownHandler not to
  120. // e.preventDefault(), because it was not allowing the event to reach the
  121. // flash player. figure out a better way to not e.preventDefault().
  122. this.setAllowTextSelection(true);
  123. // Media items don't use RTL styles, so avoid accessing computed styles to
  124. // figure out if the control is RTL.
  125. this.setRightToLeft(false);
  126. };
  127. goog.inherits(goog.ui.media.Media, goog.ui.Control);
  128. /**
  129. * The media data model used on the renderer.
  130. *
  131. * @type {goog.ui.media.MediaModel}
  132. * @private
  133. */
  134. goog.ui.media.Media.prototype.dataModel_;
  135. /**
  136. * Sets the media model to be used on the renderer.
  137. * @param {goog.ui.media.MediaModel} dataModel The media model the renderer
  138. * should use.
  139. */
  140. goog.ui.media.Media.prototype.setDataModel = function(dataModel) {
  141. this.dataModel_ = dataModel;
  142. };
  143. /**
  144. * Gets the media model renderer is using.
  145. * @return {goog.ui.media.MediaModel} The media model being used.
  146. */
  147. goog.ui.media.Media.prototype.getDataModel = function() {
  148. return this.dataModel_;
  149. };
  150. /**
  151. * Base class of all media renderers. Provides the common renderer functionality
  152. * of medias.
  153. *
  154. * The current common functionality shared by Medias is to have an outer frame
  155. * that gets highlighted on mouse hover.
  156. *
  157. * TODO(user): implement more common UI behavior, as needed.
  158. *
  159. * NOTE(user): I am not enjoying how the subclasses are changing their state
  160. * through setState() ... maybe provide abstract methods like
  161. * goog.ui.media.MediaRenderer.prototype.preview = goog.abstractMethod;
  162. * goog.ui.media.MediaRenderer.prototype.play = goog.abstractMethod;
  163. * goog.ui.media.MediaRenderer.prototype.minimize = goog.abstractMethod;
  164. * goog.ui.media.MediaRenderer.prototype.maximize = goog.abstractMethod;
  165. * and call them on this parent class setState ?
  166. *
  167. * @constructor
  168. * @extends {goog.ui.ControlRenderer}
  169. */
  170. goog.ui.media.MediaRenderer = function() {
  171. goog.ui.ControlRenderer.call(this);
  172. };
  173. goog.inherits(goog.ui.media.MediaRenderer, goog.ui.ControlRenderer);
  174. /**
  175. * Builds the common DOM structure of medias. Builds an outer div, and appends
  176. * a child div with the {@code goog.ui.Control.getContent} content. Marks the
  177. * caption with a {@code this.getClassClass()} + '-caption' css flag, so that
  178. * specific renderers can hide/show the caption as desired.
  179. *
  180. * @param {goog.ui.Control} control The control instance.
  181. * @return {!Element} The DOM structure that represents control.
  182. * @override
  183. */
  184. goog.ui.media.MediaRenderer.prototype.createDom = function(control) {
  185. goog.asserts.assertInstanceof(control, goog.ui.media.Media);
  186. var domHelper = control.getDomHelper();
  187. var div = domHelper.createElement(goog.dom.TagName.DIV);
  188. div.className = this.getClassNames(control).join(' ');
  189. var dataModel = control.getDataModel();
  190. // Only creates DOMs if the data is available.
  191. if (dataModel.getCaption()) {
  192. var caption = domHelper.createElement(goog.dom.TagName.DIV);
  193. caption.className = goog.getCssName(this.getCssClass(), 'caption');
  194. caption.appendChild(
  195. domHelper.createDom(
  196. goog.dom.TagName.P,
  197. goog.getCssName(this.getCssClass(), 'caption-text'),
  198. dataModel.getCaption()));
  199. domHelper.appendChild(div, caption);
  200. }
  201. if (dataModel.getDescription()) {
  202. var description = domHelper.createElement(goog.dom.TagName.DIV);
  203. description.className = goog.getCssName(this.getCssClass(), 'description');
  204. description.appendChild(
  205. domHelper.createDom(
  206. goog.dom.TagName.P,
  207. goog.getCssName(this.getCssClass(), 'description-text'),
  208. dataModel.getDescription()));
  209. domHelper.appendChild(div, description);
  210. }
  211. // Creates thumbnails of the media.
  212. var thumbnails = dataModel.getThumbnails() || [];
  213. for (var index = 0; index < thumbnails.length; index++) {
  214. var thumbnail = thumbnails[index];
  215. var thumbnailElement = domHelper.createElement(goog.dom.TagName.IMG);
  216. thumbnailElement.src = thumbnail.getUrl();
  217. thumbnailElement.className = this.getThumbnailCssName(index);
  218. // Check that the size is defined and that the size's height and width
  219. // are defined. Undefined height and width is deprecated but still
  220. // seems to exist in some cases.
  221. var size = thumbnail.getSize();
  222. if (size && goog.isDefAndNotNull(size.height) &&
  223. goog.isDefAndNotNull(size.width)) {
  224. goog.style.setSize(thumbnailElement, size);
  225. }
  226. domHelper.appendChild(div, thumbnailElement);
  227. }
  228. if (dataModel.getPlayer()) {
  229. // if medias have players, allow UI for a play button.
  230. var playButton = domHelper.createElement(goog.dom.TagName.DIV);
  231. playButton.className = goog.getCssName(this.getCssClass(), 'playbutton');
  232. domHelper.appendChild(div, playButton);
  233. }
  234. control.setElementInternal(div);
  235. this.setState(
  236. control,
  237. /** @type {goog.ui.Component.State} */ (control.getState()), true);
  238. return div;
  239. };
  240. /**
  241. * Returns a renamable CSS class name for a numbered thumbnail. The default
  242. * implementation generates the class names goog-ui-media-thumbnail0,
  243. * goog-ui-media-thumbnail1, and the generic goog-ui-media-thumbnailn.
  244. * Subclasses can override this method when their media requires additional
  245. * specific class names (Applications are supposed to know how many thumbnails
  246. * media will have).
  247. *
  248. * @param {number} index The thumbnail index.
  249. * @return {string} CSS class name.
  250. * @protected
  251. */
  252. goog.ui.media.MediaRenderer.prototype.getThumbnailCssName = function(index) {
  253. switch (index) {
  254. case 0:
  255. return goog.getCssName(this.getCssClass(), 'thumbnail0');
  256. case 1:
  257. return goog.getCssName(this.getCssClass(), 'thumbnail1');
  258. case 2:
  259. return goog.getCssName(this.getCssClass(), 'thumbnail2');
  260. case 3:
  261. return goog.getCssName(this.getCssClass(), 'thumbnail3');
  262. case 4:
  263. return goog.getCssName(this.getCssClass(), 'thumbnail4');
  264. default:
  265. return goog.getCssName(this.getCssClass(), 'thumbnailn');
  266. }
  267. };