Window.js 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384
  1. /**
  2. * Window.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. * Creates a new window.
  12. *
  13. * @-x-less Window.less
  14. * @class tinymce.ui.Window
  15. * @extends tinymce.ui.FloatPanel
  16. */
  17. define("tinymce/ui/Window", [
  18. "tinymce/ui/FloatPanel",
  19. "tinymce/ui/Panel",
  20. "tinymce/ui/DomUtils",
  21. "tinymce/ui/DragHelper"
  22. ], function(FloatPanel, Panel, DomUtils, DragHelper) {
  23. "use strict";
  24. var Window = FloatPanel.extend({
  25. modal: true,
  26. Defaults: {
  27. border: 1,
  28. layout: 'flex',
  29. containerCls: 'panel',
  30. role: 'dialog',
  31. callbacks: {
  32. submit: function() {
  33. this.fire('submit', {data: this.toJSON()});
  34. },
  35. close: function() {
  36. this.close();
  37. }
  38. }
  39. },
  40. /**
  41. * Constructs a instance with the specified settings.
  42. *
  43. * @constructor
  44. * @param {Object} settings Name/value object with settings.
  45. */
  46. init: function(settings) {
  47. var self = this;
  48. self._super(settings);
  49. if (self.isRtl()) {
  50. self.addClass('rtl');
  51. }
  52. self.addClass('window');
  53. self._fixed = true;
  54. // Create statusbar
  55. if (settings.buttons) {
  56. self.statusbar = new Panel({
  57. layout: 'flex',
  58. border: '1 0 0 0',
  59. spacing: 3,
  60. padding: 10,
  61. align: 'center',
  62. pack: self.isRtl() ? 'start' : 'end',
  63. defaults: {
  64. type: 'button'
  65. },
  66. items: settings.buttons
  67. });
  68. self.statusbar.addClass('foot');
  69. self.statusbar.parent(self);
  70. }
  71. self.on('click', function(e) {
  72. if (e.target.className.indexOf(self.classPrefix + 'close') != -1) {
  73. self.close();
  74. }
  75. });
  76. self.on('cancel', function() {
  77. self.close();
  78. });
  79. self.aria('describedby', self.describedBy || self._id + '-none');
  80. self.aria('label', settings.title);
  81. self._fullscreen = false;
  82. },
  83. /**
  84. * Recalculates the positions of the controls in the current container.
  85. * This is invoked by the reflow method and shouldn't be called directly.
  86. *
  87. * @method recalc
  88. */
  89. recalc: function() {
  90. var self = this, statusbar = self.statusbar, layoutRect, width, x, needsRecalc;
  91. if (self._fullscreen) {
  92. self.layoutRect(DomUtils.getWindowSize());
  93. self.layoutRect().contentH = self.layoutRect().innerH;
  94. }
  95. self._super();
  96. layoutRect = self.layoutRect();
  97. // Resize window based on title width
  98. if (self.settings.title && !self._fullscreen) {
  99. width = layoutRect.headerW;
  100. if (width > layoutRect.w) {
  101. x = layoutRect.x - Math.max(0, width / 2);
  102. self.layoutRect({w: width, x: x});
  103. needsRecalc = true;
  104. }
  105. }
  106. // Resize window based on statusbar width
  107. if (statusbar) {
  108. statusbar.layoutRect({w: self.layoutRect().innerW}).recalc();
  109. width = statusbar.layoutRect().minW + layoutRect.deltaW;
  110. if (width > layoutRect.w) {
  111. x = layoutRect.x - Math.max(0, width - layoutRect.w);
  112. self.layoutRect({w: width, x: x});
  113. needsRecalc = true;
  114. }
  115. }
  116. // Recalc body and disable auto resize
  117. if (needsRecalc) {
  118. self.recalc();
  119. }
  120. },
  121. /**
  122. * Initializes the current controls layout rect.
  123. * This will be executed by the layout managers to determine the
  124. * default minWidth/minHeight etc.
  125. *
  126. * @method initLayoutRect
  127. * @return {Object} Layout rect instance.
  128. */
  129. initLayoutRect: function() {
  130. var self = this, layoutRect = self._super(), deltaH = 0, headEl;
  131. // Reserve vertical space for title
  132. if (self.settings.title && !self._fullscreen) {
  133. headEl = self.getEl('head');
  134. var size = DomUtils.getSize(headEl);
  135. layoutRect.headerW = size.width;
  136. layoutRect.headerH = size.height;
  137. deltaH += layoutRect.headerH;
  138. }
  139. // Reserve vertical space for statusbar
  140. if (self.statusbar) {
  141. deltaH += self.statusbar.layoutRect().h;
  142. }
  143. layoutRect.deltaH += deltaH;
  144. layoutRect.minH += deltaH;
  145. //layoutRect.innerH -= deltaH;
  146. layoutRect.h += deltaH;
  147. var rect = DomUtils.getWindowSize();
  148. layoutRect.x = Math.max(0, rect.w / 2 - layoutRect.w / 2);
  149. layoutRect.y = Math.max(0, rect.h / 2 - layoutRect.h / 2);
  150. return layoutRect;
  151. },
  152. /**
  153. * Renders the control as a HTML string.
  154. *
  155. * @method renderHtml
  156. * @return {String} HTML representing the control.
  157. */
  158. renderHtml: function() {
  159. var self = this, layout = self._layout, id = self._id, prefix = self.classPrefix;
  160. var settings = self.settings, headerHtml = '', footerHtml = '', html = settings.html;
  161. self.preRender();
  162. layout.preRender(self);
  163. if (settings.title) {
  164. headerHtml = (
  165. '<div id="' + id + '-head" class="' + prefix + 'window-head">' +
  166. '<div id="' + id + '-title" class="' + prefix + 'title">' + self.encode(settings.title) + '</div>' +
  167. '<button type="button" class="' + prefix + 'close" aria-hidden="true">\u00d7</button>' +
  168. '<div id="' + id + '-dragh" class="' + prefix + 'dragh"></div>' +
  169. '</div>'
  170. );
  171. }
  172. if (settings.url) {
  173. html = '<iframe src="' + settings.url + '" tabindex="-1"></iframe>';
  174. }
  175. if (typeof(html) == "undefined") {
  176. html = layout.renderHtml(self);
  177. }
  178. if (self.statusbar) {
  179. footerHtml = self.statusbar.renderHtml();
  180. }
  181. return (
  182. '<div id="' + id + '" class="' + self.classes() + '" hidefocus="1">' +
  183. '<div class="' + self.classPrefix + 'reset" role="application">' +
  184. headerHtml +
  185. '<div id="' + id + '-body" class="' + self.classes('body') + '">' +
  186. html +
  187. '</div>' +
  188. footerHtml +
  189. '</div>' +
  190. '</div>'
  191. );
  192. },
  193. /**
  194. * Switches the window fullscreen mode.
  195. *
  196. * @method fullscreen
  197. * @param {Boolean} state True/false state.
  198. * @return {tinymce.ui.Window} Current window instance.
  199. */
  200. fullscreen: function(state) {
  201. var self = this, documentElement = document.documentElement, slowRendering, prefix = self.classPrefix, layoutRect;
  202. if (state != self._fullscreen) {
  203. DomUtils.on(window, 'resize', function() {
  204. var time;
  205. if (self._fullscreen) {
  206. // Time the layout time if it's to slow use a timeout to not hog the CPU
  207. if (!slowRendering) {
  208. time = new Date().getTime();
  209. var rect = DomUtils.getWindowSize();
  210. self.moveTo(0, 0).resizeTo(rect.w, rect.h);
  211. if ((new Date().getTime()) - time > 50) {
  212. slowRendering = true;
  213. }
  214. } else {
  215. if (!self._timer) {
  216. self._timer = setTimeout(function() {
  217. var rect = DomUtils.getWindowSize();
  218. self.moveTo(0, 0).resizeTo(rect.w, rect.h);
  219. self._timer = 0;
  220. }, 50);
  221. }
  222. }
  223. }
  224. });
  225. layoutRect = self.layoutRect();
  226. self._fullscreen = state;
  227. if (!state) {
  228. self._borderBox = self.parseBox(self.settings.border);
  229. self.getEl('head').style.display = '';
  230. layoutRect.deltaH += layoutRect.headerH;
  231. DomUtils.removeClass(documentElement, prefix + 'fullscreen');
  232. DomUtils.removeClass(document.body, prefix + 'fullscreen');
  233. self.removeClass('fullscreen');
  234. self.moveTo(self._initial.x, self._initial.y).resizeTo(self._initial.w, self._initial.h);
  235. } else {
  236. self._initial = {x: layoutRect.x, y: layoutRect.y, w: layoutRect.w, h: layoutRect.h};
  237. self._borderBox = self.parseBox('0');
  238. self.getEl('head').style.display = 'none';
  239. layoutRect.deltaH -= layoutRect.headerH + 2;
  240. DomUtils.addClass(documentElement, prefix + 'fullscreen');
  241. DomUtils.addClass(document.body, prefix + 'fullscreen');
  242. self.addClass('fullscreen');
  243. var rect = DomUtils.getWindowSize();
  244. self.moveTo(0, 0).resizeTo(rect.w, rect.h);
  245. }
  246. }
  247. return self.reflow();
  248. },
  249. /**
  250. * Called after the control has been rendered.
  251. *
  252. * @method postRender
  253. */
  254. postRender: function() {
  255. var self = this, startPos;
  256. setTimeout(function() {
  257. self.addClass('in');
  258. }, 0);
  259. self._super();
  260. if (self.statusbar) {
  261. self.statusbar.postRender();
  262. }
  263. self.focus();
  264. this.dragHelper = new DragHelper(self._id + '-dragh', {
  265. start: function() {
  266. startPos = {
  267. x: self.layoutRect().x,
  268. y: self.layoutRect().y
  269. };
  270. },
  271. drag: function(e) {
  272. self.moveTo(startPos.x + e.deltaX, startPos.y + e.deltaY);
  273. }
  274. });
  275. self.on('submit', function(e) {
  276. if (!e.isDefaultPrevented()) {
  277. self.close();
  278. }
  279. });
  280. },
  281. /**
  282. * Fires a submit event with the serialized form.
  283. *
  284. * @method submit
  285. * @return {Object} Event arguments object.
  286. */
  287. submit: function() {
  288. return this.fire('submit', {data: this.toJSON()});
  289. },
  290. /**
  291. * Removes the current control from DOM and from UI collections.
  292. *
  293. * @method remove
  294. * @return {tinymce.ui.Control} Current control instance.
  295. */
  296. remove: function() {
  297. var self = this, prefix = self.classPrefix;
  298. self.dragHelper.destroy();
  299. self._super();
  300. if (self.statusbar) {
  301. this.statusbar.remove();
  302. }
  303. if (self._fullscreen) {
  304. DomUtils.removeClass(document.documentElement, prefix + 'fullscreen');
  305. DomUtils.removeClass(document.body, prefix + 'fullscreen');
  306. }
  307. },
  308. /**
  309. * Returns the contentWindow object of the iframe if it exists.
  310. *
  311. * @method getContentWindow
  312. * @return {Window} window object or null.
  313. */
  314. getContentWindow: function() {
  315. var ifr = this.getEl().getElementsByTagName('iframe')[0];
  316. return ifr ? ifr.contentWindow : null;
  317. }
  318. });
  319. return Window;
  320. });