ControlManager.js 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524
  1. /**
  2. * ControlManager.js
  3. *
  4. * Copyright 2009, Moxiecode Systems AB
  5. * Released under LGPL License.
  6. *
  7. * License: http://tinymce.moxiecode.com/license
  8. * Contributing: http://tinymce.moxiecode.com/contributing
  9. */
  10. (function(tinymce) {
  11. // Shorten names
  12. var DOM = tinymce.DOM, Event = tinymce.dom.Event, each = tinymce.each, extend = tinymce.extend;
  13. /**
  14. * This class is responsible for managing UI control instances. It's both a factory and a collection for the controls.
  15. * @class tinymce.ControlManager
  16. */
  17. tinymce.create('tinymce.ControlManager', {
  18. /**
  19. * Constructs a new control manager instance.
  20. * Consult the Wiki for more details on this class.
  21. *
  22. * @constructor
  23. * @method ControlManager
  24. * @param {tinymce.Editor} ed TinyMCE editor instance to add the control to.
  25. * @param {Object} s Optional settings object for the control manager.
  26. */
  27. ControlManager : function(ed, s) {
  28. var t = this, i;
  29. s = s || {};
  30. t.editor = ed;
  31. t.controls = {};
  32. t.onAdd = new tinymce.util.Dispatcher(t);
  33. t.onPostRender = new tinymce.util.Dispatcher(t);
  34. t.prefix = s.prefix || ed.id + '_';
  35. t._cls = {};
  36. t.onPostRender.add(function() {
  37. each(t.controls, function(c) {
  38. c.postRender();
  39. });
  40. });
  41. },
  42. /**
  43. * Returns a control by id or undefined it it wasn't found.
  44. *
  45. * @method get
  46. * @param {String} id Control instance name.
  47. * @return {tinymce.ui.Control} Control instance or undefined.
  48. */
  49. get : function(id) {
  50. return this.controls[this.prefix + id] || this.controls[id];
  51. },
  52. /**
  53. * Sets the active state of a control by id.
  54. *
  55. * @method setActive
  56. * @param {String} id Control id to set state on.
  57. * @param {Boolean} s Active state true/false.
  58. * @return {tinymce.ui.Control} Control instance that got activated or null if it wasn't found.
  59. */
  60. setActive : function(id, s) {
  61. var c = null;
  62. if (c = this.get(id))
  63. c.setActive(s);
  64. return c;
  65. },
  66. /**
  67. * Sets the dsiabled state of a control by id.
  68. *
  69. * @method setDisabled
  70. * @param {String} id Control id to set state on.
  71. * @param {Boolean} s Active state true/false.
  72. * @return {tinymce.ui.Control} Control instance that got disabled or null if it wasn't found.
  73. */
  74. setDisabled : function(id, s) {
  75. var c = null;
  76. if (c = this.get(id))
  77. c.setDisabled(s);
  78. return c;
  79. },
  80. /**
  81. * Adds a control to the control collection inside the manager.
  82. *
  83. * @method add
  84. * @param {tinymce.ui.Control} Control instance to add to collection.
  85. * @return {tinymce.ui.Control} Control instance that got passed in.
  86. */
  87. add : function(c) {
  88. var t = this;
  89. if (c) {
  90. t.controls[c.id] = c;
  91. t.onAdd.dispatch(c, t);
  92. }
  93. return c;
  94. },
  95. /**
  96. * Creates a control by name, when a control is created it will automatically add it to the control collection.
  97. * It first ask all plugins for the specified control if the plugins didn't return a control then the default behavior
  98. * will be used.
  99. *
  100. * @method createControl
  101. * @param {String} n Control name to create for example "separator".
  102. * @return {tinymce.ui.Control} Control instance that got created and added.
  103. */
  104. createControl : function(n) {
  105. var c, t = this, ed = t.editor;
  106. each(ed.plugins, function(p) {
  107. if (p.createControl) {
  108. c = p.createControl(n, t);
  109. if (c)
  110. return false;
  111. }
  112. });
  113. switch (n) {
  114. case "|":
  115. case "separator":
  116. return t.createSeparator();
  117. }
  118. if (!c && ed.buttons && (c = ed.buttons[n]))
  119. return t.createButton(n, c);
  120. return t.add(c);
  121. },
  122. /**
  123. * Creates a drop menu control instance by id.
  124. *
  125. * @method createDropMenu
  126. * @param {String} id Unique id for the new dropdown instance. For example "some menu".
  127. * @param {Object} s Optional settings object for the control.
  128. * @param {Object} cc Optional control class to use instead of the default one.
  129. * @return {tinymce.ui.Control} Control instance that got created and added.
  130. */
  131. createDropMenu : function(id, s, cc) {
  132. var t = this, ed = t.editor, c, bm, v, cls;
  133. s = extend({
  134. 'class' : 'mceDropDown',
  135. constrain : ed.settings.constrain_menus
  136. }, s);
  137. s['class'] = s['class'] + ' ' + ed.getParam('skin') + 'Skin';
  138. if (v = ed.getParam('skin_variant'))
  139. s['class'] += ' ' + ed.getParam('skin') + 'Skin' + v.substring(0, 1).toUpperCase() + v.substring(1);
  140. id = t.prefix + id;
  141. cls = cc || t._cls.dropmenu || tinymce.ui.DropMenu;
  142. c = t.controls[id] = new cls(id, s);
  143. c.onAddItem.add(function(c, o) {
  144. var s = o.settings;
  145. s.title = ed.getLang(s.title, s.title);
  146. if (!s.onclick) {
  147. s.onclick = function(v) {
  148. if (s.cmd)
  149. ed.execCommand(s.cmd, s.ui || false, s.value);
  150. };
  151. }
  152. });
  153. ed.onRemove.add(function() {
  154. c.destroy();
  155. });
  156. // Fix for bug #1897785, #1898007
  157. if (tinymce.isIE) {
  158. c.onShowMenu.add(function() {
  159. // IE 8 needs focus in order to store away a range with the current collapsed caret location
  160. ed.focus();
  161. bm = ed.selection.getBookmark(1);
  162. });
  163. c.onHideMenu.add(function() {
  164. if (bm) {
  165. ed.selection.moveToBookmark(bm);
  166. bm = 0;
  167. }
  168. });
  169. }
  170. return t.add(c);
  171. },
  172. /**
  173. * Creates a list box control instance by id. A list box is either a native select element or a DOM/JS based list box control. This
  174. * depends on the use_native_selects settings state.
  175. *
  176. * @method createListBox
  177. * @param {String} id Unique id for the new listbox instance. For example "styles".
  178. * @param {Object} s Optional settings object for the control.
  179. * @param {Object} cc Optional control class to use instead of the default one.
  180. * @return {tinymce.ui.Control} Control instance that got created and added.
  181. */
  182. createListBox : function(id, s, cc) {
  183. var t = this, ed = t.editor, cmd, c, cls;
  184. if (t.get(id))
  185. return null;
  186. s.title = ed.translate(s.title);
  187. s.scope = s.scope || ed;
  188. if (!s.onselect) {
  189. s.onselect = function(v) {
  190. ed.execCommand(s.cmd, s.ui || false, v || s.value);
  191. };
  192. }
  193. s = extend({
  194. title : s.title,
  195. 'class' : 'mce_' + id,
  196. scope : s.scope,
  197. control_manager : t
  198. }, s);
  199. id = t.prefix + id;
  200. function useNativeListForAccessibility(ed) {
  201. return ed.settings.use_accessible_selects && !tinymce.isGecko
  202. }
  203. if (ed.settings.use_native_selects || useNativeListForAccessibility(ed))
  204. c = new tinymce.ui.NativeListBox(id, s);
  205. else {
  206. cls = cc || t._cls.listbox || tinymce.ui.ListBox;
  207. c = new cls(id, s, ed);
  208. }
  209. t.controls[id] = c;
  210. // Fix focus problem in Safari
  211. if (tinymce.isWebKit) {
  212. c.onPostRender.add(function(c, n) {
  213. // Store bookmark on mousedown
  214. Event.add(n, 'mousedown', function() {
  215. ed.bookmark = ed.selection.getBookmark(1);
  216. });
  217. // Restore on focus, since it might be lost
  218. Event.add(n, 'focus', function() {
  219. ed.selection.moveToBookmark(ed.bookmark);
  220. ed.bookmark = null;
  221. });
  222. });
  223. }
  224. if (c.hideMenu)
  225. ed.onMouseDown.add(c.hideMenu, c);
  226. return t.add(c);
  227. },
  228. /**
  229. * Creates a button control instance by id.
  230. *
  231. * @method createButton
  232. * @param {String} id Unique id for the new button instance. For example "bold".
  233. * @param {Object} s Optional settings object for the control.
  234. * @param {Object} cc Optional control class to use instead of the default one.
  235. * @return {tinymce.ui.Control} Control instance that got created and added.
  236. */
  237. createButton : function(id, s, cc) {
  238. var t = this, ed = t.editor, o, c, cls;
  239. if (t.get(id))
  240. return null;
  241. s.title = ed.translate(s.title);
  242. s.label = ed.translate(s.label);
  243. s.scope = s.scope || ed;
  244. if (!s.onclick && !s.menu_button) {
  245. s.onclick = function() {
  246. ed.execCommand(s.cmd, s.ui || false, s.value);
  247. };
  248. }
  249. s = extend({
  250. title : s.title,
  251. 'class' : 'mce_' + id,
  252. unavailable_prefix : ed.getLang('unavailable', ''),
  253. scope : s.scope,
  254. control_manager : t
  255. }, s);
  256. id = t.prefix + id;
  257. if (s.menu_button) {
  258. cls = cc || t._cls.menubutton || tinymce.ui.MenuButton;
  259. c = new cls(id, s, ed);
  260. ed.onMouseDown.add(c.hideMenu, c);
  261. } else {
  262. cls = t._cls.button || tinymce.ui.Button;
  263. c = new cls(id, s, ed);
  264. }
  265. return t.add(c);
  266. },
  267. /**
  268. * Creates a menu button control instance by id.
  269. *
  270. * @method createMenuButton
  271. * @param {String} id Unique id for the new menu button instance. For example "menu1".
  272. * @param {Object} s Optional settings object for the control.
  273. * @param {Object} cc Optional control class to use instead of the default one.
  274. * @return {tinymce.ui.Control} Control instance that got created and added.
  275. */
  276. createMenuButton : function(id, s, cc) {
  277. s = s || {};
  278. s.menu_button = 1;
  279. return this.createButton(id, s, cc);
  280. },
  281. /**
  282. * Creates a split button control instance by id.
  283. *
  284. * @method createSplitButton
  285. * @param {String} id Unique id for the new split button instance. For example "spellchecker".
  286. * @param {Object} s Optional settings object for the control.
  287. * @param {Object} cc Optional control class to use instead of the default one.
  288. * @return {tinymce.ui.Control} Control instance that got created and added.
  289. */
  290. createSplitButton : function(id, s, cc) {
  291. var t = this, ed = t.editor, cmd, c, cls;
  292. if (t.get(id))
  293. return null;
  294. s.title = ed.translate(s.title);
  295. s.scope = s.scope || ed;
  296. if (!s.onclick) {
  297. s.onclick = function(v) {
  298. ed.execCommand(s.cmd, s.ui || false, v || s.value);
  299. };
  300. }
  301. if (!s.onselect) {
  302. s.onselect = function(v) {
  303. ed.execCommand(s.cmd, s.ui || false, v || s.value);
  304. };
  305. }
  306. s = extend({
  307. title : s.title,
  308. 'class' : 'mce_' + id,
  309. scope : s.scope,
  310. control_manager : t
  311. }, s);
  312. id = t.prefix + id;
  313. cls = cc || t._cls.splitbutton || tinymce.ui.SplitButton;
  314. c = t.add(new cls(id, s, ed));
  315. ed.onMouseDown.add(c.hideMenu, c);
  316. return c;
  317. },
  318. /**
  319. * Creates a color split button control instance by id.
  320. *
  321. * @method createColorSplitButton
  322. * @param {String} id Unique id for the new color split button instance. For example "forecolor".
  323. * @param {Object} s Optional settings object for the control.
  324. * @param {Object} cc Optional control class to use instead of the default one.
  325. * @return {tinymce.ui.Control} Control instance that got created and added.
  326. */
  327. createColorSplitButton : function(id, s, cc) {
  328. var t = this, ed = t.editor, cmd, c, cls, bm;
  329. if (t.get(id))
  330. return null;
  331. s.title = ed.translate(s.title);
  332. s.scope = s.scope || ed;
  333. if (!s.onclick) {
  334. s.onclick = function(v) {
  335. if (tinymce.isIE)
  336. bm = ed.selection.getBookmark(1);
  337. ed.execCommand(s.cmd, s.ui || false, v || s.value);
  338. };
  339. }
  340. if (!s.onselect) {
  341. s.onselect = function(v) {
  342. ed.execCommand(s.cmd, s.ui || false, v || s.value);
  343. };
  344. }
  345. s = extend({
  346. title : s.title,
  347. 'class' : 'mce_' + id,
  348. 'menu_class' : ed.getParam('skin') + 'Skin',
  349. scope : s.scope,
  350. more_colors_title : ed.getLang('more_colors')
  351. }, s);
  352. id = t.prefix + id;
  353. cls = cc || t._cls.colorsplitbutton || tinymce.ui.ColorSplitButton;
  354. c = new cls(id, s, ed);
  355. ed.onMouseDown.add(c.hideMenu, c);
  356. // Remove the menu element when the editor is removed
  357. ed.onRemove.add(function() {
  358. c.destroy();
  359. });
  360. // Fix for bug #1897785, #1898007
  361. if (tinymce.isIE) {
  362. c.onShowMenu.add(function() {
  363. // IE 8 needs focus in order to store away a range with the current collapsed caret location
  364. ed.focus();
  365. bm = ed.selection.getBookmark(1);
  366. });
  367. c.onHideMenu.add(function() {
  368. if (bm) {
  369. ed.selection.moveToBookmark(bm);
  370. bm = 0;
  371. }
  372. });
  373. }
  374. return t.add(c);
  375. },
  376. /**
  377. * Creates a toolbar container control instance by id.
  378. *
  379. * @method createToolbar
  380. * @param {String} id Unique id for the new toolbar container control instance. For example "toolbar1".
  381. * @param {Object} s Optional settings object for the control.
  382. * @param {Object} cc Optional control class to use instead of the default one.
  383. * @return {tinymce.ui.Control} Control instance that got created and added.
  384. */
  385. createToolbar : function(id, s, cc) {
  386. var c, t = this, cls;
  387. id = t.prefix + id;
  388. cls = cc || t._cls.toolbar || tinymce.ui.Toolbar;
  389. c = new cls(id, s, t.editor);
  390. if (t.get(id))
  391. return null;
  392. return t.add(c);
  393. },
  394. createToolbarGroup : function(id, s, cc) {
  395. var c, t = this, cls;
  396. id = t.prefix + id;
  397. cls = cc || this._cls.toolbarGroup || tinymce.ui.ToolbarGroup;
  398. c = new cls(id, s, t.editor);
  399. if (t.get(id))
  400. return null;
  401. return t.add(c);
  402. },
  403. /**
  404. * Creates a separator control instance.
  405. *
  406. * @method createSeparator
  407. * @param {Object} cc Optional control class to use instead of the default one.
  408. * @return {tinymce.ui.Control} Control instance that got created and added.
  409. */
  410. createSeparator : function(cc) {
  411. var cls = cc || this._cls.separator || tinymce.ui.Separator;
  412. return new cls();
  413. },
  414. /**
  415. * Overrides a specific control type with a custom class.
  416. *
  417. * @method setControlType
  418. * @param {string} n Name of the control to override for example button or dropmenu.
  419. * @param {function} c Class reference to use instead of the default one.
  420. * @return {function} Same as the class reference.
  421. */
  422. setControlType : function(n, c) {
  423. return this._cls[n.toLowerCase()] = c;
  424. },
  425. /**
  426. * Destroy.
  427. *
  428. * @method destroy
  429. */
  430. destroy : function() {
  431. each(this.controls, function(c) {
  432. c.destroy();
  433. });
  434. this.controls = null;
  435. }
  436. });
  437. })(tinymce);