ZIndexManager.js 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537
  1. /**
  2. * A class that manages a group of {@link Ext.Component#floating} Components and provides z-order management,
  3. * and Component activation behavior, including masking below the active (topmost) Component.
  4. *
  5. * {@link Ext.Component#floating Floating} Components which are rendered directly into the document (such as
  6. * {@link Ext.window.Window Window}s) which are {@link Ext.Component#method-show show}n are managed by a
  7. * {@link Ext.WindowManager global instance}.
  8. *
  9. * {@link Ext.Component#floating Floating} Components which are descendants of {@link Ext.Component#floating floating}
  10. * *Containers* (for example a {@link Ext.view.BoundList BoundList} within an {@link Ext.window.Window Window},
  11. * or a {@link Ext.menu.Menu Menu}), are managed by a ZIndexManager owned by that floating Container. Therefore
  12. * ComboBox dropdowns within Windows will have managed z-indices guaranteed to be correct, relative to the Window.
  13. */
  14. Ext.define('Ext.ZIndexManager', {
  15. alternateClassName: 'Ext.WindowGroup',
  16. statics: {
  17. zBase : 9000
  18. },
  19. constructor: function(container) {
  20. var me = this;
  21. me.list = {};
  22. me.zIndexStack = [];
  23. me.front = null;
  24. if (container) {
  25. // This is the ZIndexManager for an Ext.container.Container, base its zseed on the zIndex of the Container's element
  26. if (container.isContainer) {
  27. container.on('resize', me._onContainerResize, me);
  28. me.zseed = Ext.Number.from(me.rendered ? container.getEl().getStyle('zIndex') : undefined, me.getNextZSeed());
  29. // The containing element we will be dealing with (eg masking) is the content target
  30. me.targetEl = container.getTargetEl();
  31. me.container = container;
  32. }
  33. // This is the ZIndexManager for a DOM element
  34. else {
  35. Ext.EventManager.onWindowResize(me._onContainerResize, me);
  36. me.zseed = me.getNextZSeed();
  37. me.targetEl = Ext.get(container);
  38. }
  39. }
  40. // No container passed means we are the global WindowManager. Our target is the doc body.
  41. // DOM must be ready to collect that ref.
  42. else {
  43. Ext.EventManager.onWindowResize(me._onContainerResize, me);
  44. me.zseed = me.getNextZSeed();
  45. Ext.onDocumentReady(function() {
  46. me.targetEl = Ext.getBody();
  47. });
  48. }
  49. },
  50. getNextZSeed: function() {
  51. return (Ext.ZIndexManager.zBase += 10000);
  52. },
  53. setBase: function(baseZIndex) {
  54. this.zseed = baseZIndex;
  55. var result = this.assignZIndices();
  56. this._activateLast();
  57. return result;
  58. },
  59. // private
  60. assignZIndices: function() {
  61. var a = this.zIndexStack,
  62. len = a.length,
  63. i = 0,
  64. zIndex = this.zseed,
  65. comp;
  66. for (; i < len; i++) {
  67. comp = a[i];
  68. if (comp && !comp.hidden) {
  69. // Setting the zIndex of a Component returns the topmost zIndex consumed by
  70. // that Component.
  71. // If it's just a plain floating Component such as a BoundList, then the
  72. // return value is the passed value plus 10, ready for the next item.
  73. // If a floating *Container* has its zIndex set, it re-orders its managed
  74. // floating children, starting from that new base, and returns a value 10000 above
  75. // the highest zIndex which it allocates.
  76. zIndex = comp.setZIndex(zIndex);
  77. }
  78. }
  79. // Activate new topmost
  80. this._activateLast();
  81. return zIndex;
  82. },
  83. // private
  84. _setActiveChild: function(comp, oldFront) {
  85. var front = this.front;
  86. if (comp !== front) {
  87. if (front && !front.destroying) {
  88. front.setActive(false, comp);
  89. }
  90. this.front = comp;
  91. if (comp && comp != oldFront) {
  92. comp.setActive(true);
  93. if (comp.modal) {
  94. this._showModalMask(comp);
  95. }
  96. }
  97. }
  98. },
  99. onComponentHide: function(comp){
  100. comp.setActive(false);
  101. this._activateLast();
  102. },
  103. // private
  104. _activateLast: function() {
  105. var me = this,
  106. stack = me.zIndexStack,
  107. i = stack.length - 1,
  108. oldFront = me.front,
  109. comp;
  110. // There may be no visible floater to activate
  111. me.front = undefined;
  112. // Go down through the z-index stack.
  113. // Activate the next visible one down.
  114. // If that was modal, then we're done
  115. for (; i >= 0 && stack[i].hidden; --i);
  116. if ((comp = stack[i])) {
  117. me._setActiveChild(comp, oldFront);
  118. if (comp.modal) {
  119. return;
  120. }
  121. }
  122. // If the new top one was not modal, keep going down to find the next visible
  123. // modal one to shift the modal mask down under
  124. for (; i >= 0; --i) {
  125. comp = stack[i];
  126. // If we find a visible modal further down the zIndex stack, move the mask to just under it.
  127. if (comp.isVisible() && comp.modal) {
  128. me._showModalMask(comp);
  129. return;
  130. }
  131. }
  132. // No visible modal Component was found in the run down the stack.
  133. // So hide the modal mask
  134. me._hideModalMask();
  135. },
  136. _showModalMask: function(comp) {
  137. var me = this,
  138. zIndex = comp.el.getStyle('zIndex') - 4,
  139. maskTarget = comp.floatParent ? comp.floatParent.getTargetEl() : comp.container,
  140. viewSize = maskTarget.getBox();
  141. if (maskTarget.dom === document.body) {
  142. viewSize.height = Math.max(document.body.scrollHeight, Ext.dom.Element.getDocumentHeight());
  143. viewSize.width = Math.max(document.body.scrollWidth, viewSize.width);
  144. }
  145. if (!me.mask) {
  146. me.mask = Ext.getBody().createChild({
  147. cls: Ext.baseCSSPrefix + 'mask'
  148. });
  149. me.mask.setVisibilityMode(Ext.Element.DISPLAY);
  150. me.mask.on('click', me._onMaskClick, me);
  151. }
  152. me.mask.maskTarget = maskTarget;
  153. maskTarget.addCls(Ext.baseCSSPrefix + 'body-masked');
  154. me.mask.setStyle('zIndex', zIndex);
  155. // setting mask box before showing it in an IE7 strict iframe within a quirks page
  156. // can cause body scrolling [EXTJSIV-6219]
  157. me.mask.show();
  158. me.mask.setBox(viewSize);
  159. },
  160. _hideModalMask: function() {
  161. var mask = this.mask;
  162. if (mask && mask.isVisible()) {
  163. mask.maskTarget.removeCls(Ext.baseCSSPrefix + 'body-masked');
  164. mask.maskTarget = undefined;
  165. mask.hide();
  166. }
  167. },
  168. _onMaskClick: function() {
  169. if (this.front) {
  170. this.front.focus();
  171. }
  172. },
  173. _onContainerResize: function() {
  174. var mask = this.mask,
  175. maskTarget,
  176. viewSize;
  177. if (mask && mask.isVisible()) {
  178. // At the new container size, the mask might be *causing* the scrollbar, so to find the valid
  179. // client size to mask, we must temporarily unmask the parent node.
  180. mask.hide();
  181. maskTarget = mask.maskTarget;
  182. if (maskTarget.dom === document.body) {
  183. viewSize = {
  184. height: Math.max(document.body.scrollHeight, Ext.dom.Element.getDocumentHeight()),
  185. width: Math.max(document.body.scrollWidth, document.documentElement.clientWidth)
  186. };
  187. } else {
  188. viewSize = maskTarget.getViewSize(true);
  189. }
  190. mask.setSize(viewSize);
  191. mask.show();
  192. }
  193. },
  194. /**
  195. * Registers a floating {@link Ext.Component} with this ZIndexManager. This should not
  196. * need to be called under normal circumstances. Floating Components (such as Windows,
  197. * BoundLists and Menus) are automatically registered with a
  198. * {@link Ext.Component#zIndexManager zIndexManager} at render time.
  199. *
  200. * Where this may be useful is moving Windows between two ZIndexManagers. For example,
  201. * to bring the Ext.MessageBox dialog under the same manager as the Desktop's
  202. * ZIndexManager in the desktop sample app:
  203. *
  204. * MyDesktop.getDesktop().getManager().register(Ext.MessageBox);
  205. *
  206. * @param {Ext.Component} comp The Component to register.
  207. */
  208. register : function(comp) {
  209. var me = this;
  210. if (comp.zIndexManager) {
  211. comp.zIndexManager.unregister(comp);
  212. }
  213. comp.zIndexManager = me;
  214. me.list[comp.id] = comp;
  215. me.zIndexStack.push(comp);
  216. comp.on('hide', me.onComponentHide, me);
  217. },
  218. /**
  219. * Unregisters a {@link Ext.Component} from this ZIndexManager. This should not
  220. * need to be called. Components are automatically unregistered upon destruction.
  221. * See {@link #register}.
  222. * @param {Ext.Component} comp The Component to unregister.
  223. */
  224. unregister : function(comp) {
  225. var me = this,
  226. list = me.list;
  227. delete comp.zIndexManager;
  228. if (list && list[comp.id]) {
  229. delete list[comp.id];
  230. comp.un('hide', me.onComponentHide);
  231. Ext.Array.remove(me.zIndexStack, comp);
  232. // Destruction requires that the topmost visible floater be activated. Same as hiding.
  233. me._activateLast();
  234. }
  235. },
  236. /**
  237. * Gets a registered Component by id.
  238. * @param {String/Object} id The id of the Component or a {@link Ext.Component} instance
  239. * @return {Ext.Component}
  240. */
  241. get : function(id) {
  242. return id.isComponent ? id : this.list[id];
  243. },
  244. /**
  245. * Brings the specified Component to the front of any other active Components in this ZIndexManager.
  246. * @param {String/Object} comp The id of the Component or a {@link Ext.Component} instance
  247. * @return {Boolean} True if the dialog was brought to the front, else false
  248. * if it was already in front
  249. */
  250. bringToFront : function(comp) {
  251. var me = this,
  252. result = false,
  253. zIndexStack = me.zIndexStack;
  254. comp = me.get(comp);
  255. if (comp !== me.front) {
  256. Ext.Array.remove(zIndexStack, comp);
  257. if (comp.preventBringToFront) {
  258. // this takes care of cases where a load mask should be displayed under a floated component
  259. zIndexStack.unshift(comp);
  260. } else {
  261. // the default behavior is to push onto the stack
  262. zIndexStack.push(comp);
  263. }
  264. me.assignZIndices();
  265. result = true;
  266. this.front = comp;
  267. }
  268. if (result && comp.modal) {
  269. me._showModalMask(comp);
  270. }
  271. return result;
  272. },
  273. /**
  274. * Sends the specified Component to the back of other active Components in this ZIndexManager.
  275. * @param {String/Object} comp The id of the Component or a {@link Ext.Component} instance
  276. * @return {Ext.Component} The Component
  277. */
  278. sendToBack : function(comp) {
  279. var me = this;
  280. comp = me.get(comp);
  281. Ext.Array.remove(me.zIndexStack, comp);
  282. me.zIndexStack.unshift(comp);
  283. me.assignZIndices();
  284. this._activateLast();
  285. return comp;
  286. },
  287. /**
  288. * Hides all Components managed by this ZIndexManager.
  289. */
  290. hideAll : function() {
  291. var list = this.list,
  292. item,
  293. id;
  294. for (id in list) {
  295. if (list.hasOwnProperty(id)) {
  296. item = list[id];
  297. if (item.isComponent && item.isVisible()) {
  298. item.hide();
  299. }
  300. }
  301. }
  302. },
  303. /**
  304. * @private
  305. * Temporarily hides all currently visible managed Components. This is for when
  306. * dragging a Window which may manage a set of floating descendants in its ZIndexManager;
  307. * they should all be hidden just for the duration of the drag.
  308. */
  309. hide: function() {
  310. var me = this,
  311. mask = me.mask,
  312. i = 0,
  313. stack = me.zIndexStack,
  314. len = stack.length,
  315. comp;
  316. me.tempHidden = me.tempHidden||[];
  317. for (; i < len; i++) {
  318. comp = stack[i];
  319. if (comp.isVisible()) {
  320. me.tempHidden.push(comp);
  321. comp.el.hide();
  322. }
  323. }
  324. // Also hide modal mask during hidden state
  325. if (mask) {
  326. mask.hide();
  327. }
  328. },
  329. /**
  330. * @private
  331. * Restores temporarily hidden managed Components to visibility.
  332. */
  333. show: function() {
  334. var me = this,
  335. mask = me.mask,
  336. i = 0,
  337. tempHidden = me.tempHidden,
  338. len = tempHidden ? tempHidden.length : 0,
  339. comp;
  340. for (; i < len; i++) {
  341. comp = tempHidden[i];
  342. comp.el.show();
  343. comp.setPosition(comp.x, comp.y);
  344. }
  345. me.tempHidden.length = 0;
  346. // Also restore mask to visibility and ensure it is aligned with its target element
  347. if (mask) {
  348. mask.show();
  349. mask.alignTo(mask.maskTarget, 'tl-tl');
  350. }
  351. },
  352. /**
  353. * Gets the currently-active Component in this ZIndexManager.
  354. * @return {Ext.Component} The active Component
  355. */
  356. getActive : function() {
  357. return this.front;
  358. },
  359. /**
  360. * Returns zero or more Components in this ZIndexManager using the custom search function passed to this method.
  361. * The function should accept a single {@link Ext.Component} reference as its only argument and should
  362. * return true if the Component matches the search criteria, otherwise it should return false.
  363. * @param {Function} fn The search function
  364. * @param {Object} [scope] The scope (this reference) in which the function is executed.
  365. * Defaults to the Component being tested. That gets passed to the function if not specified.
  366. * @return {Array} An array of zero or more matching windows
  367. */
  368. getBy : function(fn, scope) {
  369. var r = [],
  370. i = 0,
  371. stack = this.zIndexStack,
  372. len = stack.length,
  373. comp;
  374. for (; i < len; i++) {
  375. comp = stack[i];
  376. if (fn.call(scope||comp, comp) !== false) {
  377. r.push(comp);
  378. }
  379. }
  380. return r;
  381. },
  382. /**
  383. * Executes the specified function once for every Component in this ZIndexManager, passing each
  384. * Component as the only parameter. Returning false from the function will stop the iteration.
  385. * @param {Function} fn The function to execute for each item
  386. * @param {Object} [scope] The scope (this reference) in which the function
  387. * is executed. Defaults to the current Component in the iteration.
  388. */
  389. each : function(fn, scope) {
  390. var list = this.list,
  391. id,
  392. comp;
  393. for (id in list) {
  394. if (list.hasOwnProperty(id)) {
  395. comp = list[id];
  396. if (comp.isComponent && fn.call(scope || comp, comp) === false) {
  397. return;
  398. }
  399. }
  400. }
  401. },
  402. /**
  403. * Executes the specified function once for every Component in this ZIndexManager, passing each
  404. * Component as the only parameter. Returning false from the function will stop the iteration.
  405. * The components are passed to the function starting at the bottom and proceeding to the top.
  406. * @param {Function} fn The function to execute for each item
  407. * @param {Object} scope (optional) The scope (this reference) in which the function
  408. * is executed. Defaults to the current Component in the iteration.
  409. */
  410. eachBottomUp: function (fn, scope) {
  411. var stack = this.zIndexStack,
  412. i = 0,
  413. len = stack.length,
  414. comp;
  415. for (; i < len; i++) {
  416. comp = stack[i];
  417. if (comp.isComponent && fn.call(scope || comp, comp) === false) {
  418. return;
  419. }
  420. }
  421. },
  422. /**
  423. * Executes the specified function once for every Component in this ZIndexManager, passing each
  424. * Component as the only parameter. Returning false from the function will stop the iteration.
  425. * The components are passed to the function starting at the top and proceeding to the bottom.
  426. * @param {Function} fn The function to execute for each item
  427. * @param {Object} [scope] The scope (this reference) in which the function
  428. * is executed. Defaults to the current Component in the iteration.
  429. */
  430. eachTopDown: function (fn, scope) {
  431. var stack = this.zIndexStack,
  432. i = stack.length,
  433. comp;
  434. for (; i-- > 0; ) {
  435. comp = stack[i];
  436. if (comp.isComponent && fn.call(scope || comp, comp) === false) {
  437. return;
  438. }
  439. }
  440. },
  441. destroy: function() {
  442. var me = this,
  443. list = me.list,
  444. comp,
  445. id;
  446. for (id in list) {
  447. if (list.hasOwnProperty(id)) {
  448. comp = list[id];
  449. if (comp.isComponent) {
  450. comp.destroy();
  451. }
  452. }
  453. }
  454. delete me.zIndexStack;
  455. delete me.list;
  456. delete me.container;
  457. delete me.targetEl;
  458. }
  459. }, function() {
  460. /**
  461. * @class Ext.WindowManager
  462. * @extends Ext.ZIndexManager
  463. *
  464. * The default global floating Component group that is available automatically.
  465. *
  466. * This manages instances of floating Components which were rendered programatically without
  467. * being added to a {@link Ext.container.Container Container}, and for floating Components
  468. * which were added into non-floating Containers.
  469. *
  470. * *Floating* Containers create their own instance of ZIndexManager, and floating Components
  471. * added at any depth below there are managed by that ZIndexManager.
  472. *
  473. * @singleton
  474. */
  475. Ext.WindowManager = Ext.WindowMgr = new this();
  476. });