FloatPanel.js 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366
  1. /**
  2. * FloatPanel.js
  3. *
  4. * Copyright, Moxiecode Systems AB
  5. * Released under LGPL License.
  6. *
  7. * License: http://www.tinymce.com/license
  8. * Contributing: http://www.tinymce.com/contributing
  9. */
  10. /**
  11. * This class creates a floating panel.
  12. *
  13. * @-x-less FloatPanel.less
  14. * @class tinymce.ui.FloatPanel
  15. * @extends tinymce.ui.Panel
  16. * @mixes tinymce.ui.Movable
  17. * @mixes tinymce.ui.Resizable
  18. */
  19. define("tinymce/ui/FloatPanel", [
  20. "tinymce/ui/Panel",
  21. "tinymce/ui/Movable",
  22. "tinymce/ui/Resizable",
  23. "tinymce/ui/DomUtils"
  24. ], function(Panel, Movable, Resizable, DomUtils) {
  25. "use strict";
  26. var documentClickHandler, documentScrollHandler, visiblePanels = [];
  27. var zOrder = [], hasModal;
  28. var FloatPanel = Panel.extend({
  29. Mixins: [Movable, Resizable],
  30. /**
  31. * Constructs a new control instance with the specified settings.
  32. *
  33. * @constructor
  34. * @param {Object} settings Name/value object with settings.
  35. * @setting {Boolean} autohide Automatically hide the panel.
  36. */
  37. init: function(settings) {
  38. var self = this;
  39. function reorder() {
  40. var i, zIndex = FloatPanel.zIndex || 0xFFFF, topModal;
  41. if (zOrder.length) {
  42. for (i = 0; i < zOrder.length; i++) {
  43. if (zOrder[i].modal) {
  44. zIndex++;
  45. topModal = zOrder[i];
  46. }
  47. zOrder[i].getEl().style.zIndex = zIndex;
  48. zOrder[i].zIndex = zIndex;
  49. zIndex++;
  50. }
  51. }
  52. var modalBlockEl = document.getElementById(self.classPrefix + 'modal-block');
  53. if (topModal) {
  54. DomUtils.css(modalBlockEl, 'z-index', topModal.zIndex - 1);
  55. } else if (modalBlockEl) {
  56. modalBlockEl.parentNode.removeChild(modalBlockEl);
  57. hasModal = false;
  58. }
  59. FloatPanel.currentZIndex = zIndex;
  60. }
  61. function isChildOf(ctrl, parent) {
  62. while (ctrl) {
  63. if (ctrl == parent) {
  64. return true;
  65. }
  66. ctrl = ctrl.parent();
  67. }
  68. }
  69. /**
  70. * Repositions the panel to the top of page if the panel is outside of the visual viewport. It will
  71. * also reposition all child panels of the current panel.
  72. */
  73. function repositionPanel(panel) {
  74. var scrollY = DomUtils.getViewPort().y;
  75. function toggleFixedChildPanels(fixed, deltaY) {
  76. var parent;
  77. for (var i = 0; i < visiblePanels.length; i++) {
  78. if (visiblePanels[i] != panel) {
  79. parent = visiblePanels[i].parent();
  80. while (parent && (parent = parent.parent())) {
  81. if (parent == panel) {
  82. visiblePanels[i].fixed(fixed).moveBy(0, deltaY).repaint();
  83. }
  84. }
  85. }
  86. }
  87. }
  88. if (panel.settings.autofix) {
  89. if (!panel._fixed) {
  90. panel._autoFixY = panel.layoutRect().y;
  91. if (panel._autoFixY < scrollY) {
  92. panel.fixed(true).layoutRect({y: 0}).repaint();
  93. toggleFixedChildPanels(true, scrollY - panel._autoFixY);
  94. }
  95. } else {
  96. if (panel._autoFixY > scrollY) {
  97. panel.fixed(false).layoutRect({y: panel._autoFixY}).repaint();
  98. toggleFixedChildPanels(false, panel._autoFixY - scrollY);
  99. }
  100. }
  101. }
  102. }
  103. self._super(settings);
  104. self._eventsRoot = self;
  105. self.addClass('floatpanel');
  106. // Hide floatpanes on click out side the root button
  107. if (settings.autohide) {
  108. if (!documentClickHandler) {
  109. documentClickHandler = function(e) {
  110. // Hide any float panel when a click is out side that float panel and the
  111. // float panels direct parent for example a click on a menu button
  112. var i = visiblePanels.length;
  113. while (i--) {
  114. var panel = visiblePanels[i], clickCtrl = panel.getParentCtrl(e.target);
  115. if (panel.settings.autohide) {
  116. if (clickCtrl) {
  117. if (isChildOf(clickCtrl, panel) || panel.parent() === clickCtrl) {
  118. continue;
  119. }
  120. }
  121. e = panel.fire('autohide', {target: e.target});
  122. if (!e.isDefaultPrevented()) {
  123. panel.hide();
  124. }
  125. }
  126. }
  127. };
  128. DomUtils.on(document, 'click', documentClickHandler);
  129. }
  130. visiblePanels.push(self);
  131. }
  132. if (settings.autofix) {
  133. if (!documentScrollHandler) {
  134. documentScrollHandler = function() {
  135. var i;
  136. i = visiblePanels.length;
  137. while (i--) {
  138. repositionPanel(visiblePanels[i]);
  139. }
  140. };
  141. DomUtils.on(window, 'scroll', documentScrollHandler);
  142. }
  143. self.on('move', function() {
  144. repositionPanel(this);
  145. });
  146. }
  147. self.on('postrender show', function(e) {
  148. if (e.control == self) {
  149. var modalBlockEl, prefix = self.classPrefix;
  150. if (self.modal && !hasModal) {
  151. modalBlockEl = DomUtils.createFragment('<div id="' + prefix + 'modal-block" class="' +
  152. prefix + 'reset ' + prefix + 'fade"></div>');
  153. modalBlockEl = modalBlockEl.firstChild;
  154. self.getContainerElm().appendChild(modalBlockEl);
  155. setTimeout(function() {
  156. DomUtils.addClass(modalBlockEl, prefix + 'in');
  157. DomUtils.addClass(self.getEl(), prefix + 'in');
  158. }, 0);
  159. hasModal = true;
  160. }
  161. zOrder.push(self);
  162. reorder();
  163. }
  164. });
  165. self.on('close hide', function(e) {
  166. if (e.control == self) {
  167. var i = zOrder.length;
  168. while (i--) {
  169. if (zOrder[i] === self) {
  170. zOrder.splice(i, 1);
  171. }
  172. }
  173. reorder();
  174. }
  175. });
  176. self.on('show', function() {
  177. self.parents().each(function(ctrl) {
  178. if (ctrl._fixed) {
  179. self.fixed(true);
  180. return false;
  181. }
  182. });
  183. });
  184. if (settings.popover) {
  185. self._preBodyHtml = '<div class="' + self.classPrefix + 'arrow"></div>';
  186. self.addClass('popover').addClass('bottom').addClass(self.isRtl() ? 'end' : 'start');
  187. }
  188. },
  189. fixed: function(state) {
  190. var self = this;
  191. if (self._fixed != state) {
  192. if (self._rendered) {
  193. var viewport = DomUtils.getViewPort();
  194. if (state) {
  195. self.layoutRect().y -= viewport.y;
  196. } else {
  197. self.layoutRect().y += viewport.y;
  198. }
  199. }
  200. self.toggleClass('fixed', state);
  201. self._fixed = state;
  202. }
  203. return self;
  204. },
  205. /**
  206. * Shows the current float panel.
  207. *
  208. * @method show
  209. * @return {tinymce.ui.FloatPanel} Current floatpanel instance.
  210. */
  211. show: function() {
  212. var self = this, i, state = self._super();
  213. i = visiblePanels.length;
  214. while (i--) {
  215. if (visiblePanels[i] === self) {
  216. break;
  217. }
  218. }
  219. if (i === -1) {
  220. visiblePanels.push(self);
  221. }
  222. return state;
  223. },
  224. /**
  225. * Hides the current float panel.
  226. *
  227. * @method hide
  228. * @return {tinymce.ui.FloatPanel} Current floatpanel instance.
  229. */
  230. hide: function() {
  231. removeVisiblePanel(this);
  232. return this._super();
  233. },
  234. /**
  235. * Hides all visible the float panels.
  236. *
  237. * @method hideAll
  238. */
  239. hideAll: function() {
  240. FloatPanel.hideAll();
  241. },
  242. /**
  243. * Closes the float panel. This will remove the float panel from page and fire the close event.
  244. *
  245. * @method close
  246. */
  247. close: function() {
  248. var self = this;
  249. self.fire('close');
  250. return self.remove();
  251. },
  252. /**
  253. * Removes the float panel from page.
  254. *
  255. * @method remove
  256. */
  257. remove: function() {
  258. removeVisiblePanel(this);
  259. this._super();
  260. },
  261. postRender: function() {
  262. var self = this;
  263. if (self.settings.bodyRole) {
  264. this.getEl('body').setAttribute('role', self.settings.bodyRole);
  265. }
  266. return self._super();
  267. }
  268. });
  269. /**
  270. * Hides all visible the float panels.
  271. *
  272. * @static
  273. * @method hideAll
  274. */
  275. FloatPanel.hideAll = function() {
  276. var i = visiblePanels.length;
  277. while (i--) {
  278. var panel = visiblePanels[i];
  279. if (panel && panel.settings.autohide) {
  280. panel.hide();
  281. visiblePanels.splice(i, 1);
  282. }
  283. }
  284. };
  285. function removeVisiblePanel(panel) {
  286. var i;
  287. i = visiblePanels.length;
  288. while (i--) {
  289. if (visiblePanels[i] === panel) {
  290. visiblePanels.splice(i, 1);
  291. }
  292. }
  293. i = zOrder.length;
  294. while (i--) {
  295. if (zOrder[i] === panel) {
  296. zOrder.splice(i, 1);
  297. }
  298. }
  299. }
  300. return FloatPanel;
  301. });