toolbarfactory.js 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433
  1. // Copyright 2008 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 Generic factory functions for creating the building blocks for
  16. * an editor toolbar.
  17. *
  18. * @author attila@google.com (Attila Bodis)
  19. */
  20. goog.provide('goog.ui.editor.ToolbarFactory');
  21. goog.require('goog.array');
  22. goog.require('goog.dom');
  23. goog.require('goog.dom.TagName');
  24. goog.require('goog.string');
  25. goog.require('goog.string.Unicode');
  26. goog.require('goog.style');
  27. goog.require('goog.ui.Component');
  28. goog.require('goog.ui.Container');
  29. goog.require('goog.ui.Option');
  30. goog.require('goog.ui.Toolbar');
  31. goog.require('goog.ui.ToolbarButton');
  32. goog.require('goog.ui.ToolbarColorMenuButton');
  33. goog.require('goog.ui.ToolbarMenuButton');
  34. goog.require('goog.ui.ToolbarRenderer');
  35. goog.require('goog.ui.ToolbarSelect');
  36. goog.require('goog.userAgent');
  37. /**
  38. * Takes a font spec (e.g. "Arial, Helvetica, sans-serif") and returns the
  39. * primary font name, normalized to lowercase (e.g. "arial").
  40. * @param {string} fontSpec Font specification.
  41. * @return {string} The primary font name, in lowercase.
  42. */
  43. goog.ui.editor.ToolbarFactory.getPrimaryFont = function(fontSpec) {
  44. var i = fontSpec.indexOf(',');
  45. var fontName = (i != -1 ? fontSpec.substring(0, i) : fontSpec).toLowerCase();
  46. // Strip leading/trailing quotes from the font name (bug 1050118).
  47. return goog.string.stripQuotes(fontName, '"\'');
  48. };
  49. /**
  50. * Bulk-adds fonts to the given font menu button. The argument must be an
  51. * array of font descriptor objects, each of which must have the following
  52. * attributes:
  53. * <ul>
  54. * <li>{@code caption} - Caption to show in the font menu (e.g. 'Tahoma')
  55. * <li>{@code value} - Value for the corresponding 'font-family' CSS style
  56. * (e.g. 'Tahoma, Arial, sans-serif')
  57. * </ul>
  58. * @param {!goog.ui.Select} button Font menu button.
  59. * @param {!Array<{caption: string, value: string}>} fonts Array of
  60. * font descriptors.
  61. */
  62. goog.ui.editor.ToolbarFactory.addFonts = function(button, fonts) {
  63. goog.array.forEach(fonts, function(font) {
  64. goog.ui.editor.ToolbarFactory.addFont(button, font.caption, font.value);
  65. });
  66. };
  67. /**
  68. * Adds a menu item to the given font menu button. The first font listed in
  69. * the {@code value} argument is considered the font ID, so adding two items
  70. * whose CSS style starts with the same font may lead to unpredictable results.
  71. * @param {!goog.ui.Select} button Font menu button.
  72. * @param {string} caption Caption to show for the font menu.
  73. * @param {string} value Value for the corresponding 'font-family' CSS style.
  74. */
  75. goog.ui.editor.ToolbarFactory.addFont = function(button, caption, value) {
  76. // The font ID is the first font listed in the CSS style, normalized to
  77. // lowercase.
  78. var id = goog.ui.editor.ToolbarFactory.getPrimaryFont(value);
  79. // Construct the option, and add it to the button.
  80. var option = new goog.ui.Option(caption, value, button.getDomHelper());
  81. option.setId(id);
  82. button.addItem(option);
  83. // Captions are shown in their own font.
  84. option.getContentElement().style.fontFamily = value;
  85. };
  86. /**
  87. * Bulk-adds font sizes to the given font size menu button. The argument must
  88. * be an array of font size descriptor objects, each of which must have the
  89. * following attributes:
  90. * <ul>
  91. * <li>{@code caption} - Caption to show in the font size menu (e.g. 'Huge')
  92. * <li>{@code value} - Value for the corresponding HTML font size (e.g. 6)
  93. * </ul>
  94. * @param {!goog.ui.Select} button Font size menu button.
  95. * @param {!Array<{caption: string, value:number}>} sizes Array of font
  96. * size descriptors.
  97. */
  98. goog.ui.editor.ToolbarFactory.addFontSizes = function(button, sizes) {
  99. goog.array.forEach(sizes, function(size) {
  100. goog.ui.editor.ToolbarFactory.addFontSize(button, size.caption, size.value);
  101. });
  102. };
  103. /**
  104. * Adds a menu item to the given font size menu button. The {@code value}
  105. * argument must be a legacy HTML font size in the 0-7 range.
  106. * @param {!goog.ui.Select} button Font size menu button.
  107. * @param {string} caption Caption to show in the font size menu.
  108. * @param {number} value Value for the corresponding HTML font size.
  109. */
  110. goog.ui.editor.ToolbarFactory.addFontSize = function(button, caption, value) {
  111. // Construct the option, and add it to the button.
  112. var option = new goog.ui.Option(caption, value, button.getDomHelper());
  113. button.addItem(option);
  114. // Adjust the font size of the menu item and the height of the checkbox
  115. // element after they've been rendered by addItem(). Captions are shown in
  116. // the corresponding font size, and lining up the checkbox is tricky.
  117. var content = option.getContentElement();
  118. content.style.fontSize =
  119. goog.ui.editor.ToolbarFactory.getPxFromLegacySize(value) + 'px';
  120. content.firstChild.style.height = '1.1em';
  121. };
  122. /**
  123. * Converts a legacy font size specification into an equivalent pixel size.
  124. * For example, {@code &lt;font size="6"&gt;} is {@code font-size: 32px;}, etc.
  125. * @param {number} fontSize Legacy font size spec in the 0-7 range.
  126. * @return {number} Equivalent pixel size.
  127. */
  128. goog.ui.editor.ToolbarFactory.getPxFromLegacySize = function(fontSize) {
  129. return goog.ui.editor.ToolbarFactory.LEGACY_SIZE_TO_PX_MAP_[fontSize] || 10;
  130. };
  131. /**
  132. * Converts a pixel font size specification into an equivalent legacy size.
  133. * For example, {@code font-size: 32px;} is {@code &lt;font size="6"&gt;}, etc.
  134. * If the given pixel size doesn't exactly match one of the legacy sizes, -1 is
  135. * returned.
  136. * @param {number} px Pixel font size.
  137. * @return {number} Equivalent legacy size spec in the 0-7 range, or -1 if none
  138. * exists.
  139. */
  140. goog.ui.editor.ToolbarFactory.getLegacySizeFromPx = function(px) {
  141. // Use lastIndexOf to get the largest legacy size matching the pixel size
  142. // (most notably returning 1 instead of 0 for 10px).
  143. return goog.array.lastIndexOf(
  144. goog.ui.editor.ToolbarFactory.LEGACY_SIZE_TO_PX_MAP_, px);
  145. };
  146. /**
  147. * Map of legacy font sizes (0-7) to equivalent pixel sizes.
  148. * @type {!Array<number>}
  149. * @private
  150. */
  151. goog.ui.editor.ToolbarFactory.LEGACY_SIZE_TO_PX_MAP_ =
  152. [10, 10, 13, 16, 18, 24, 32, 48];
  153. /**
  154. * Bulk-adds format options to the given "Format block" menu button. The
  155. * argument must be an array of format option descriptor objects, each of
  156. * which must have the following attributes:
  157. * <ul>
  158. * <li>{@code caption} - Caption to show in the menu (e.g. 'Minor heading')
  159. * <li>{@code command} - Corresponding {@link goog.dom.TagName} (e.g.
  160. * 'H4')
  161. * </ul>
  162. * @param {!goog.ui.Select} button "Format block" menu button.
  163. * @param {!Array<{caption: string, command: !goog.dom.TagName}>} formats Array
  164. * of format option descriptors.
  165. */
  166. goog.ui.editor.ToolbarFactory.addFormatOptions = function(button, formats) {
  167. goog.array.forEach(formats, function(format) {
  168. goog.ui.editor.ToolbarFactory.addFormatOption(
  169. button, format.caption, format.command);
  170. });
  171. };
  172. /**
  173. * Adds a menu item to the given "Format block" menu button.
  174. * @param {!goog.ui.Select} button "Format block" menu button.
  175. * @param {string} caption Caption to show in the menu.
  176. * @param {!goog.dom.TagName} tag Corresponding block format tag.
  177. */
  178. goog.ui.editor.ToolbarFactory.addFormatOption = function(button, caption, tag) {
  179. // Construct the option, and add it to the button.
  180. // TODO(attila): Create boring but functional menu item for now...
  181. var buttonDom = button.getDomHelper();
  182. var option = new goog.ui.Option(
  183. buttonDom.createDom(goog.dom.TagName.DIV, null, caption), tag, buttonDom);
  184. option.setId(String(tag));
  185. button.addItem(option);
  186. };
  187. /**
  188. * Creates a {@link goog.ui.Toolbar} containing the specified set of
  189. * toolbar buttons, and renders it into the given parent element. Each
  190. * item in the {@code items} array must a {@link goog.ui.Control}.
  191. * @param {!Array<goog.ui.Control>} items Toolbar items; each must
  192. * be a {@link goog.ui.Control}.
  193. * @param {!Element} elem Toolbar parent element.
  194. * @param {boolean=} opt_isRightToLeft Whether the editor chrome is
  195. * right-to-left; defaults to the directionality of the toolbar parent
  196. * element.
  197. * @return {!goog.ui.Toolbar} Editor toolbar, rendered into the given parent
  198. * element.
  199. */
  200. goog.ui.editor.ToolbarFactory.makeToolbar = function(
  201. items, elem, opt_isRightToLeft) {
  202. var domHelper = goog.dom.getDomHelper(elem);
  203. // Create an empty horizontal toolbar using the default renderer.
  204. var toolbar = new goog.ui.Toolbar(
  205. goog.ui.ToolbarRenderer.getInstance(),
  206. goog.ui.Container.Orientation.HORIZONTAL, domHelper);
  207. // Optimization: Explicitly test for the directionality of the parent
  208. // element here, so we can set it for both the toolbar and its children,
  209. // saving a lot of expensive calls to goog.style.isRightToLeft() during
  210. // rendering.
  211. var isRightToLeft = opt_isRightToLeft || goog.style.isRightToLeft(elem);
  212. toolbar.setRightToLeft(isRightToLeft);
  213. // Optimization: Set the toolbar to non-focusable before it is rendered,
  214. // to avoid creating unnecessary keyboard event handler objects.
  215. toolbar.setFocusable(false);
  216. for (var i = 0, button; button = items[i]; i++) {
  217. // Optimization: Set the button to non-focusable before it is rendered,
  218. // to avoid creating unnecessary keyboard event handler objects. Also set
  219. // the directionality of the button explicitly, to avoid expensive calls
  220. // to goog.style.isRightToLeft() during rendering.
  221. button.setSupportedState(goog.ui.Component.State.FOCUSED, false);
  222. button.setRightToLeft(isRightToLeft);
  223. toolbar.addChild(button, true);
  224. }
  225. toolbar.render(elem);
  226. return toolbar;
  227. };
  228. /**
  229. * Creates a toolbar button with the given ID, tooltip, and caption. Applies
  230. * any custom CSS class names to the button's caption element.
  231. * @param {string} id Button ID; must equal a {@link goog.editor.Command} for
  232. * built-in buttons, anything else for custom buttons.
  233. * @param {string} tooltip Tooltip to be shown on hover.
  234. * @param {goog.ui.ControlContent} caption Button caption.
  235. * @param {string=} opt_classNames CSS class name(s) to apply to the caption
  236. * element.
  237. * @param {goog.ui.ButtonRenderer=} opt_renderer Button renderer; defaults to
  238. * {@link goog.ui.ToolbarButtonRenderer} if unspecified.
  239. * @param {goog.dom.DomHelper=} opt_domHelper DOM helper, used for DOM
  240. * creation; defaults to the current document if unspecified.
  241. * @return {!goog.ui.Button} A toolbar button.
  242. */
  243. goog.ui.editor.ToolbarFactory.makeButton = function(
  244. id, tooltip, caption, opt_classNames, opt_renderer, opt_domHelper) {
  245. var button = new goog.ui.ToolbarButton(
  246. goog.ui.editor.ToolbarFactory.createContent_(
  247. caption, opt_classNames, opt_domHelper),
  248. opt_renderer, opt_domHelper);
  249. button.setId(id);
  250. button.setTooltip(tooltip);
  251. return button;
  252. };
  253. /**
  254. * Creates a toggle button with the given ID, tooltip, and caption. Applies
  255. * any custom CSS class names to the button's caption element. The button
  256. * returned has checkbox-like toggle semantics.
  257. * @param {string} id Button ID; must equal a {@link goog.editor.Command} for
  258. * built-in buttons, anything else for custom buttons.
  259. * @param {string} tooltip Tooltip to be shown on hover.
  260. * @param {goog.ui.ControlContent} caption Button caption.
  261. * @param {string=} opt_classNames CSS class name(s) to apply to the caption
  262. * element.
  263. * @param {goog.ui.ButtonRenderer=} opt_renderer Button renderer; defaults to
  264. * {@link goog.ui.ToolbarButtonRenderer} if unspecified.
  265. * @param {goog.dom.DomHelper=} opt_domHelper DOM helper, used for DOM
  266. * creation; defaults to the current document if unspecified.
  267. * @return {!goog.ui.Button} A toggle button.
  268. */
  269. goog.ui.editor.ToolbarFactory.makeToggleButton = function(
  270. id, tooltip, caption, opt_classNames, opt_renderer, opt_domHelper) {
  271. var button = goog.ui.editor.ToolbarFactory.makeButton(
  272. id, tooltip, caption, opt_classNames, opt_renderer, opt_domHelper);
  273. button.setSupportedState(goog.ui.Component.State.CHECKED, true);
  274. return button;
  275. };
  276. /**
  277. * Creates a menu button with the given ID, tooltip, and caption. Applies
  278. * any custom CSS class names to the button's caption element. The button
  279. * returned doesn't have an actual menu attached; use {@link
  280. * goog.ui.MenuButton#setMenu} to attach a {@link goog.ui.Menu} to the
  281. * button.
  282. * @param {string} id Button ID; must equal a {@link goog.editor.Command} for
  283. * built-in buttons, anything else for custom buttons.
  284. * @param {string} tooltip Tooltip to be shown on hover.
  285. * @param {goog.ui.ControlContent} caption Button caption.
  286. * @param {string=} opt_classNames CSS class name(s) to apply to the caption
  287. * element.
  288. * @param {goog.ui.ButtonRenderer=} opt_renderer Button renderer; defaults to
  289. * {@link goog.ui.ToolbarMenuButtonRenderer} if unspecified.
  290. * @param {goog.dom.DomHelper=} opt_domHelper DOM helper, used for DOM
  291. * creation; defaults to the current document if unspecified.
  292. * @return {!goog.ui.MenuButton} A menu button.
  293. */
  294. goog.ui.editor.ToolbarFactory.makeMenuButton = function(
  295. id, tooltip, caption, opt_classNames, opt_renderer, opt_domHelper) {
  296. var button = new goog.ui.ToolbarMenuButton(
  297. goog.ui.editor.ToolbarFactory.createContent_(
  298. caption, opt_classNames, opt_domHelper),
  299. null, opt_renderer, opt_domHelper);
  300. button.setId(id);
  301. button.setTooltip(tooltip);
  302. return button;
  303. };
  304. /**
  305. * Creates a select button with the given ID, tooltip, and caption. Applies
  306. * any custom CSS class names to the button's root element. The button
  307. * returned doesn't have an actual menu attached; use {@link
  308. * goog.ui.Select#setMenu} to attach a {@link goog.ui.Menu} containing
  309. * {@link goog.ui.Option}s to the select button.
  310. * @param {string} id Button ID; must equal a {@link goog.editor.Command} for
  311. * built-in buttons, anything else for custom buttons.
  312. * @param {string} tooltip Tooltip to be shown on hover.
  313. * @param {goog.ui.ControlContent} caption Button caption; used as the
  314. * default caption when nothing is selected.
  315. * @param {string=} opt_classNames CSS class name(s) to apply to the button's
  316. * root element.
  317. * @param {goog.ui.MenuButtonRenderer=} opt_renderer Button renderer;
  318. * defaults to {@link goog.ui.ToolbarMenuButtonRenderer} if unspecified.
  319. * @param {goog.dom.DomHelper=} opt_domHelper DOM helper, used for DOM
  320. * creation; defaults to the current document if unspecified.
  321. * @return {!goog.ui.Select} A select button.
  322. */
  323. goog.ui.editor.ToolbarFactory.makeSelectButton = function(
  324. id, tooltip, caption, opt_classNames, opt_renderer, opt_domHelper) {
  325. var button =
  326. new goog.ui.ToolbarSelect(null, null, opt_renderer, opt_domHelper);
  327. if (opt_classNames) {
  328. // Unlike the other button types, for goog.ui.Select buttons we apply the
  329. // extra class names to the root element, because for select buttons the
  330. // caption isn't stable (as it changes each time the selection changes).
  331. goog.array.forEach(
  332. opt_classNames.split(/\s+/), button.addClassName, button);
  333. }
  334. button.addClassName(goog.getCssName('goog-toolbar-select'));
  335. button.setDefaultCaption(caption);
  336. button.setId(id);
  337. button.setTooltip(tooltip);
  338. return button;
  339. };
  340. /**
  341. * Creates a color menu button with the given ID, tooltip, and caption.
  342. * Applies any custom CSS class names to the button's caption element. The
  343. * button is created with a default color menu containing standard color
  344. * palettes.
  345. * @param {string} id Button ID; must equal a {@link goog.editor.Command} for
  346. * built-in toolbar buttons, but can be anything else for custom buttons.
  347. * @param {string} tooltip Tooltip to be shown on hover.
  348. * @param {goog.ui.ControlContent} caption Button caption.
  349. * @param {string=} opt_classNames CSS class name(s) to apply to the caption
  350. * element.
  351. * @param {goog.ui.ColorMenuButtonRenderer=} opt_renderer Button renderer;
  352. * defaults to {@link goog.ui.ToolbarColorMenuButtonRenderer}
  353. * if unspecified.
  354. * @param {goog.dom.DomHelper=} opt_domHelper DOM helper, used for DOM
  355. * creation; defaults to the current document if unspecified.
  356. * @return {!goog.ui.ColorMenuButton} A color menu button.
  357. */
  358. goog.ui.editor.ToolbarFactory.makeColorMenuButton = function(
  359. id, tooltip, caption, opt_classNames, opt_renderer, opt_domHelper) {
  360. var button = new goog.ui.ToolbarColorMenuButton(
  361. goog.ui.editor.ToolbarFactory.createContent_(
  362. caption, opt_classNames, opt_domHelper),
  363. null, opt_renderer, opt_domHelper);
  364. button.setId(id);
  365. button.setTooltip(tooltip);
  366. return button;
  367. };
  368. /**
  369. * Creates a new DIV that wraps a button caption, optionally applying CSS
  370. * class names to it. Used as a helper function in button factory methods.
  371. * @param {goog.ui.ControlContent} caption Button caption.
  372. * @param {string=} opt_classNames CSS class name(s) to apply to the DIV that
  373. * wraps the caption (if any).
  374. * @param {goog.dom.DomHelper=} opt_domHelper DOM helper, used for DOM
  375. * creation; defaults to the current document if unspecified.
  376. * @return {!Element} DIV that wraps the caption.
  377. * @private
  378. */
  379. goog.ui.editor.ToolbarFactory.createContent_ = function(
  380. caption, opt_classNames, opt_domHelper) {
  381. // FF2 doesn't like empty DIVs, especially when rendered right-to-left.
  382. if ((!caption || caption == '') && goog.userAgent.GECKO &&
  383. !goog.userAgent.isVersionOrHigher('1.9a')) {
  384. caption = goog.string.Unicode.NBSP;
  385. }
  386. return (opt_domHelper || goog.dom.getDomHelper())
  387. .createDom(goog.dom.TagName.DIV, opt_classNames, caption);
  388. };