FullScreen.js 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457
  1. define("dijit/_editor/plugins/FullScreen", [
  2. "dojo/aspect",
  3. "dojo/_base/declare", // declare
  4. "dojo/dom-class", // domClass.add domClass.remove
  5. "dojo/dom-geometry",
  6. "dojo/dom-style",
  7. "dojo/_base/event", // event.stop
  8. "dojo/i18n", // i18n.getLocalization
  9. "dojo/keys", // keys.F11 keys.TAB
  10. "dojo/_base/lang", // lang.hitch
  11. "dojo/on", // on()
  12. "dojo/_base/sniff", // has("ie"), has("quirks")
  13. "dojo/_base/window", // win.body
  14. "dojo/window", // winUtils.getBox winUtils.scrollIntoView
  15. "../../focus", // focus.focus(), focus.curNode
  16. "../_Plugin",
  17. "../../form/ToggleButton",
  18. "../../registry", // registry.getEnclosingWidget()
  19. "dojo/i18n!../nls/commands"
  20. ], function(aspect, declare, domClass, domGeometry, domStyle, event, i18n, keys, lang, on, has, win, winUtils,
  21. focus, _Plugin, ToggleButton, registry){
  22. /*=====
  23. var _Plugin = dijit._editor._Plugin;
  24. =====*/
  25. // module:
  26. // dijit/_editor/plugins/FullScreen
  27. // summary:
  28. // This plugin provides FullScreen capability to the editor. When
  29. // toggled on, it will render the editor into the full window and
  30. // overlay everything. It also binds to the hotkey: CTRL-SHIFT-F11
  31. // for toggling fullscreen mode.
  32. var FullScreen = declare("dijit._editor.plugins.FullScreen",_Plugin,{
  33. // summary:
  34. // This plugin provides FullScreen capability to the editor. When
  35. // toggled on, it will render the editor into the full window and
  36. // overlay everything. It also binds to the hotkey: CTRL-SHIFT-F11
  37. // for toggling fullscreen mode.
  38. // zIndex: [public] Number
  39. // zIndex value used for overlaying the full page.
  40. // default is 500.
  41. zIndex: 500,
  42. // _origState: [private] Object
  43. // The original view state of the editor.
  44. _origState: null,
  45. // _origiFrameState: [private] Object
  46. // The original view state of the iframe of the editor.
  47. _origiFrameState: null,
  48. // _resizeHandle: [private] Object
  49. // Connection point used for handling resize when window resizes.
  50. _resizeHandle: null,
  51. // isFullscreen: [const] boolean
  52. // Read-Only variable used to denote of the editor is in fullscreen mode or not.
  53. isFullscreen: false,
  54. toggle: function(){
  55. // summary:
  56. // Function to allow programmatic toggling of the view.
  57. this.button.set("checked", !this.button.get("checked"));
  58. },
  59. _initButton: function(){
  60. // summary:
  61. // Over-ride for creation of the resize button.
  62. var strings = i18n.getLocalization("dijit._editor", "commands"),
  63. editor = this.editor;
  64. this.button = new ToggleButton({
  65. label: strings["fullScreen"],
  66. dir: editor.dir,
  67. lang: editor.lang,
  68. showLabel: false,
  69. iconClass: this.iconClassPrefix + " " + this.iconClassPrefix + "FullScreen",
  70. tabIndex: "-1",
  71. onChange: lang.hitch(this, "_setFullScreen")
  72. });
  73. },
  74. setEditor: function(editor){
  75. // summary:
  76. // Over-ride for the setting of the editor.
  77. // editor: Object
  78. // The editor to configure for this plugin to use.
  79. this.editor = editor;
  80. this._initButton();
  81. this.editor.addKeyHandler(keys.F11, true, true, lang.hitch(this, function(e){
  82. // Enable the CTRL-SHIFT-F11 hotkey for fullscreen mode.
  83. this.toggle();
  84. event.stop(e);
  85. setTimeout(lang.hitch(this, function(){this.editor.focus();}), 250);
  86. return true;
  87. }));
  88. this.connect(this.editor.domNode, "onkeydown", "_containFocus");
  89. },
  90. _containFocus: function(e){
  91. // summary:
  92. // When in Full Screen mode, it's good to try and retain focus in the editor
  93. // so this function is intended to try and constrain the TAB key.
  94. // e: Event
  95. // The key event.
  96. // tags:
  97. // private
  98. if(this.isFullscreen){
  99. var ed = this.editor;
  100. if(!ed.isTabIndent &&
  101. ed._fullscreen_oldOnKeyDown &&
  102. e.keyCode === keys.TAB){
  103. // If we're in fullscreen mode, we want to take over how tab moves focus a bit.
  104. // to keep it within the editor since it's hiding the rest of the page.
  105. // IE hates changing focus IN the event handler, so need to put calls
  106. // in a timeout. Gotta love IE.
  107. // Also need to check for alternate view nodes if present and active.
  108. var f = focus.curNode;
  109. var avn = this._getAltViewNode();
  110. if(f == ed.iframe ||
  111. (avn && f === avn)){
  112. setTimeout(lang.hitch(this, function(){
  113. ed.toolbar.focus();
  114. }), 10);
  115. }else{
  116. if(avn && domStyle.get(ed.iframe, "display") === "none"){
  117. setTimeout(lang.hitch(this, function(){
  118. focus.focus(avn);
  119. }), 10);
  120. }else{
  121. setTimeout(lang.hitch(this, function(){
  122. ed.focus();
  123. }), 10);
  124. }
  125. }
  126. event.stop(e);
  127. }else if(ed._fullscreen_oldOnKeyDown){
  128. // Only call up when it's a different function. Traps corner case event issue
  129. // on IE which caused stack overflow on handler cleanup.
  130. ed._fullscreen_oldOnKeyDown(e);
  131. }
  132. }
  133. },
  134. _resizeEditor: function(){
  135. // summary:
  136. // Function to handle resizing the editor as the viewport
  137. // resizes (window scaled)
  138. // tags:
  139. // private
  140. var vp = winUtils.getBox();
  141. domGeometry.setMarginBox(this.editor.domNode, {
  142. w: vp.w,
  143. h: vp.h
  144. });
  145. //Adjust the internal heights too, as they can be a bit off.
  146. var hHeight = this.editor.getHeaderHeight();
  147. var fHeight = this.editor.getFooterHeight();
  148. var extents = domGeometry.getPadBorderExtents(this.editor.domNode);
  149. var fcpExtents = domGeometry.getPadBorderExtents(this.editor.iframe.parentNode);
  150. var fcmExtents = domGeometry.getMarginExtents(this.editor.iframe.parentNode);
  151. var cHeight = vp.h - (hHeight + extents.h + fHeight);
  152. domGeometry.setMarginBox(this.editor.iframe.parentNode, {
  153. h: cHeight,
  154. w: vp.w
  155. });
  156. domGeometry.setMarginBox(this.editor.iframe, {
  157. h: cHeight - (fcpExtents.h + fcmExtents.h)
  158. });
  159. },
  160. _getAltViewNode: function(){
  161. // summary:
  162. // This function is intended as a hook point for setting an
  163. // alternate view node for when in full screen mode and the
  164. // editable iframe is hidden.
  165. // tags:
  166. // protected.
  167. },
  168. _setFullScreen: function(full){
  169. // summary:
  170. // Function to handle toggling between full screen and
  171. // regular view.
  172. // tags:
  173. // private
  174. var vp = winUtils.getBox();
  175. //Alias this for shorter code.
  176. var ed = this.editor;
  177. var body = win.body();
  178. var editorParent = ed.domNode.parentNode;
  179. this.isFullscreen = full;
  180. if(full){
  181. //Parent classes can royally screw up this plugin, so we
  182. //have to set everything to position static.
  183. while(editorParent && editorParent !== win.body()){
  184. domClass.add(editorParent, "dijitForceStatic");
  185. editorParent = editorParent.parentNode;
  186. }
  187. // Save off the resize function. We want to kill its behavior.
  188. this._editorResizeHolder = this.editor.resize;
  189. ed.resize = function(){} ;
  190. // Try to constrain focus control.
  191. ed._fullscreen_oldOnKeyDown = ed.onKeyDown;
  192. ed.onKeyDown = lang.hitch(this, this._containFocus);
  193. this._origState = {};
  194. this._origiFrameState = {};
  195. // Store the basic editor state we have to restore later.
  196. // Not using domStyle.get here, had problems, didn't
  197. // give me stuff like 100%, gave me pixel calculated values.
  198. // Need the exact original values.
  199. var domNode = ed.domNode,
  200. rawStyle = domNode && domNode.style || {};
  201. this._origState = {
  202. width: rawStyle.width || "",
  203. height: rawStyle.height || "",
  204. top: domStyle.get(domNode, "top") || "",
  205. left: domStyle.get(domNode, "left") || "",
  206. position: domStyle.get(domNode, "position") || "static",
  207. marginBox: domGeometry.getMarginBox(ed.domNode)
  208. };
  209. // Store the iframe state we have to restore later.
  210. // Not using domStyle.get here, had problems, didn't
  211. // give me stuff like 100%, gave me pixel calculated values.
  212. // Need the exact original values.
  213. var iframe = ed.iframe,
  214. iframeStyle = iframe && iframe.style || {};
  215. var bc = domStyle.get(ed.iframe, "backgroundColor");
  216. this._origiFrameState = {
  217. backgroundColor: bc || "transparent",
  218. width: iframeStyle.width || "auto",
  219. height: iframeStyle.height || "auto",
  220. zIndex: iframeStyle.zIndex || ""
  221. };
  222. // Okay, size everything.
  223. domStyle.set(ed.domNode, {
  224. position: "absolute",
  225. top: "0px",
  226. left: "0px",
  227. zIndex: this.zIndex,
  228. width: vp.w + "px",
  229. height: vp.h + "px"
  230. });
  231. domStyle.set(ed.iframe, {
  232. height: "100%",
  233. width: "100%",
  234. zIndex: this.zIndex,
  235. backgroundColor: bc !== "transparent" &&
  236. bc !== "rgba(0, 0, 0, 0)"?bc:"white"
  237. });
  238. domStyle.set(ed.iframe.parentNode, {
  239. height: "95%",
  240. width: "100%"
  241. });
  242. // Store the overflow state we have to restore later.
  243. // IE had issues, so have to check that it's defined. Ugh.
  244. if(body.style && body.style.overflow){
  245. this._oldOverflow = domStyle.get(body, "overflow");
  246. }else{
  247. this._oldOverflow = "";
  248. }
  249. if(has("ie") && !has("quirks")){
  250. // IE will put scrollbars in anyway, html (parent of body)
  251. // also controls them in standards mode, so we have to
  252. // remove them, argh.
  253. if(body.parentNode &&
  254. body.parentNode.style &&
  255. body.parentNode.style.overflow){
  256. this._oldBodyParentOverflow = body.parentNode.style.overflow;
  257. }else{
  258. try{
  259. this._oldBodyParentOverflow = domStyle.get(body.parentNode, "overflow");
  260. }catch(e){
  261. this._oldBodyParentOverflow = "scroll";
  262. }
  263. }
  264. domStyle.set(body.parentNode, "overflow", "hidden");
  265. }
  266. domStyle.set(body, "overflow", "hidden");
  267. var resizer = function(){
  268. // function to handle resize events.
  269. // Will check current VP and only resize if
  270. // different.
  271. var vp = winUtils.getBox();
  272. if("_prevW" in this && "_prevH" in this){
  273. // No actual size change, ignore.
  274. if(vp.w === this._prevW && vp.h === this._prevH){
  275. return;
  276. }
  277. }else{
  278. this._prevW = vp.w;
  279. this._prevH = vp.h;
  280. }
  281. if(this._resizer){
  282. clearTimeout(this._resizer);
  283. delete this._resizer;
  284. }
  285. // Timeout it to help avoid spamming resize on IE.
  286. // Works for all browsers.
  287. this._resizer = setTimeout(lang.hitch(this, function(){
  288. delete this._resizer;
  289. this._resizeEditor();
  290. }), 10);
  291. };
  292. this._resizeHandle = on(window, "resize", lang.hitch(this, resizer));
  293. // Also monitor for direct calls to resize and adapt editor.
  294. this._resizeHandle2 = aspect.after(ed, "onResize", lang.hitch(this, function(){
  295. if(this._resizer){
  296. clearTimeout(this._resizer);
  297. delete this._resizer;
  298. }
  299. this._resizer = setTimeout(lang.hitch(this, function(){
  300. delete this._resizer;
  301. this._resizeEditor();
  302. }), 10);
  303. }));
  304. // Call it once to work around IE glitchiness. Safe for other browsers too.
  305. this._resizeEditor();
  306. var dn = this.editor.toolbar.domNode;
  307. setTimeout(function(){winUtils.scrollIntoView(dn);}, 250);
  308. }else{
  309. if(this._resizeHandle){
  310. // Cleanup resizing listeners
  311. this._resizeHandle.remove();
  312. this._resizeHandle = null;
  313. }
  314. if(this._resizeHandle2){
  315. // Cleanup resizing listeners
  316. this._resizeHandle2.remove();
  317. this._resizeHandle2 = null;
  318. }
  319. if(this._rst){
  320. clearTimeout(this._rst);
  321. this._rst = null;
  322. }
  323. //Remove all position static class assigns.
  324. while(editorParent && editorParent !== win.body()){
  325. domClass.remove(editorParent, "dijitForceStatic");
  326. editorParent = editorParent.parentNode;
  327. }
  328. // Restore resize function
  329. if(this._editorResizeHolder){
  330. this.editor.resize = this._editorResizeHolder;
  331. }
  332. if(!this._origState && !this._origiFrameState){
  333. // If we actually didn't toggle, then don't do anything.
  334. return;
  335. }
  336. if(ed._fullscreen_oldOnKeyDown){
  337. ed.onKeyDown = ed._fullscreen_oldOnKeyDown;
  338. delete ed._fullscreen_oldOnKeyDown;
  339. }
  340. // Add a timeout to make sure we don't have a resize firing in the
  341. // background at the time of minimize.
  342. var self = this;
  343. setTimeout(function(){
  344. // Restore all the editor state.
  345. var mb = self._origState.marginBox;
  346. var oh = self._origState.height;
  347. if(has("ie") && !has("quirks")){
  348. body.parentNode.style.overflow = self._oldBodyParentOverflow;
  349. delete self._oldBodyParentOverflow;
  350. }
  351. domStyle.set(body, "overflow", self._oldOverflow);
  352. delete self._oldOverflow;
  353. domStyle.set(ed.domNode, self._origState);
  354. domStyle.set(ed.iframe.parentNode, {
  355. height: "",
  356. width: ""
  357. });
  358. domStyle.set(ed.iframe, self._origiFrameState);
  359. delete self._origState;
  360. delete self._origiFrameState;
  361. // In case it is contained in a layout and the layout changed size,
  362. // go ahead and call resize.
  363. var pWidget = registry.getEnclosingWidget(ed.domNode.parentNode);
  364. if(pWidget && pWidget.resize){
  365. pWidget.resize();
  366. }else{
  367. if(!oh || oh.indexOf("%") < 0){
  368. // Resize if the original size wasn't set
  369. // or wasn't in percent. Timeout is to avoid
  370. // an IE crash in unit testing.
  371. setTimeout(lang.hitch(this, function(){ed.resize({h: mb.h});}), 0);
  372. }
  373. }
  374. winUtils.scrollIntoView(self.editor.toolbar.domNode);
  375. }, 100);
  376. }
  377. },
  378. updateState: function(){
  379. // summary:
  380. // Over-ride for button state control for disabled to work.
  381. this.button.set("disabled", this.get("disabled"));
  382. },
  383. destroy: function(){
  384. // summary:
  385. // Over-ride to ensure the resize handle gets cleaned up.
  386. if(this._resizeHandle){
  387. // Cleanup resizing listeners
  388. this._resizeHandle.remove();
  389. this._resizeHandle = null;
  390. }
  391. if(this._resizeHandle2){
  392. // Cleanup resizing listeners
  393. this._resizeHandle2.remove();
  394. this._resizeHandle2 = null;
  395. }
  396. if(this._resizer){
  397. clearTimeout(this._resizer);
  398. this._resizer = null;
  399. }
  400. this.inherited(arguments);
  401. }
  402. });
  403. // Register this plugin.
  404. // For back-compat accept "fullscreen" (all lowercase) too, remove in 2.0
  405. _Plugin.registry["fullScreen"] = _Plugin.registry["fullscreen"] = function(args){
  406. return new FullScreen({
  407. zIndex: ("zIndex" in args)?args.zIndex:500
  408. });
  409. };
  410. return FullScreen;
  411. });