1
0

playground.js 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426
  1. /// <reference path="../../release/monaco.d.ts" />
  2. (function () {
  3. 'use strict';
  4. var isMac = /Mac/i.test(navigator.userAgent);
  5. window.onload = function () {
  6. require(['vs/editor/editor.main'], function () {
  7. xhr('playground/monaco.d.ts.txt').then(function (response) {
  8. monaco.languages.typescript.javascriptDefaults.addExtraLib(
  9. response.responseText,
  10. 'ts:monaco.d.ts'
  11. );
  12. monaco.languages.typescript.javascriptDefaults.addExtraLib(
  13. [
  14. 'declare var require: {',
  15. ' toUrl(path: string): string;',
  16. ' (moduleName: string): any;',
  17. ' (dependencies: string[], callback: (...args: any[]) => any, errorback?: (err: any) => void): any;',
  18. ' config(data: any): any;',
  19. ' onError: Function;',
  20. '};'
  21. ].join('\n'),
  22. 'ts:require.d.ts'
  23. );
  24. });
  25. var loading = document.getElementById('loading');
  26. loading.parentNode.removeChild(loading);
  27. load();
  28. });
  29. };
  30. var editor = null;
  31. var data = {
  32. js: {
  33. model: null,
  34. state: null
  35. },
  36. css: {
  37. model: null,
  38. state: null
  39. },
  40. html: {
  41. model: null,
  42. state: null
  43. }
  44. };
  45. function load() {
  46. function layout() {
  47. var GLOBAL_PADDING = 20;
  48. var WIDTH = window.innerWidth - 2 * GLOBAL_PADDING;
  49. var HEIGHT = window.innerHeight;
  50. var TITLE_HEIGHT = 110;
  51. var FOOTER_HEIGHT = 80;
  52. var TABS_HEIGHT = 20;
  53. var INNER_PADDING = 20;
  54. var SWITCHER_HEIGHT = 30;
  55. var HALF_WIDTH = Math.floor((WIDTH - INNER_PADDING) / 2);
  56. var REMAINING_HEIGHT = HEIGHT - TITLE_HEIGHT - FOOTER_HEIGHT - SWITCHER_HEIGHT;
  57. playgroundContainer.style.width = WIDTH + 'px';
  58. playgroundContainer.style.height = HEIGHT - FOOTER_HEIGHT + 'px';
  59. sampleSwitcher.style.position = 'absolute';
  60. sampleSwitcher.style.top = TITLE_HEIGHT + 'px';
  61. sampleSwitcher.style.left = GLOBAL_PADDING + 'px';
  62. typingContainer.style.position = 'absolute';
  63. typingContainer.style.top = GLOBAL_PADDING + TITLE_HEIGHT + SWITCHER_HEIGHT + 'px';
  64. typingContainer.style.left = GLOBAL_PADDING + 'px';
  65. typingContainer.style.width = HALF_WIDTH + 'px';
  66. typingContainer.style.height = REMAINING_HEIGHT + 'px';
  67. tabArea.style.position = 'absolute';
  68. tabArea.style.boxSizing = 'border-box';
  69. tabArea.style.top = 0;
  70. tabArea.style.left = 0;
  71. tabArea.style.width = HALF_WIDTH + 'px';
  72. tabArea.style.height = TABS_HEIGHT + 'px';
  73. editorContainer.style.position = 'absolute';
  74. editorContainer.style.boxSizing = 'border-box';
  75. editorContainer.style.top = TABS_HEIGHT + 'px';
  76. editorContainer.style.left = 0;
  77. editorContainer.style.width = HALF_WIDTH + 'px';
  78. editorContainer.style.height = REMAINING_HEIGHT - TABS_HEIGHT + 'px';
  79. if (editor) {
  80. editor.layout({
  81. width: HALF_WIDTH - 2,
  82. height: REMAINING_HEIGHT - TABS_HEIGHT - 1
  83. });
  84. }
  85. runContainer.style.position = 'absolute';
  86. runContainer.style.top = GLOBAL_PADDING + TITLE_HEIGHT + SWITCHER_HEIGHT + TABS_HEIGHT + 'px';
  87. runContainer.style.left = GLOBAL_PADDING + INNER_PADDING + HALF_WIDTH + 'px';
  88. runContainer.style.width = HALF_WIDTH + 'px';
  89. runContainer.style.height = REMAINING_HEIGHT - TABS_HEIGHT + 'px';
  90. runIframeHeight = REMAINING_HEIGHT - TABS_HEIGHT;
  91. if (runIframe) {
  92. runIframe.style.height = runIframeHeight + 'px';
  93. }
  94. }
  95. function changeTab(selectedTabNode, desiredModelId) {
  96. for (var i = 0; i < tabArea.childNodes.length; i++) {
  97. var child = tabArea.childNodes[i];
  98. if (/tab/.test(child.className)) {
  99. child.className = 'tab';
  100. }
  101. }
  102. selectedTabNode.className = 'tab active';
  103. var currentState = editor.saveViewState();
  104. var currentModel = editor.getModel();
  105. if (currentModel === data.js.model) {
  106. data.js.state = currentState;
  107. } else if (currentModel === data.css.model) {
  108. data.css.state = currentState;
  109. } else if (currentModel === data.html.model) {
  110. data.html.state = currentState;
  111. }
  112. editor.setModel(data[desiredModelId].model);
  113. editor.restoreViewState(data[desiredModelId].state);
  114. editor.focus();
  115. }
  116. // create the typing side
  117. var typingContainer = document.createElement('div');
  118. typingContainer.className = 'typingContainer';
  119. var tabArea = (function () {
  120. var tabArea = document.createElement('div');
  121. tabArea.className = 'tabArea';
  122. var jsTab = document.createElement('span');
  123. jsTab.className = 'tab active';
  124. jsTab.appendChild(document.createTextNode('JavaScript'));
  125. jsTab.onclick = function () {
  126. changeTab(jsTab, 'js');
  127. };
  128. tabArea.appendChild(jsTab);
  129. var cssTab = document.createElement('span');
  130. cssTab.className = 'tab';
  131. cssTab.appendChild(document.createTextNode('CSS'));
  132. cssTab.onclick = function () {
  133. changeTab(cssTab, 'css');
  134. };
  135. tabArea.appendChild(cssTab);
  136. var htmlTab = document.createElement('span');
  137. htmlTab.className = 'tab';
  138. htmlTab.appendChild(document.createTextNode('HTML'));
  139. htmlTab.onclick = function () {
  140. changeTab(htmlTab, 'html');
  141. };
  142. tabArea.appendChild(htmlTab);
  143. var runLabel = 'Press ' + (isMac ? 'CMD + return' : 'CTRL + Enter') + ' to run the code.';
  144. var runBtn = document.createElement('button');
  145. runBtn.className = 'action run';
  146. runBtn.setAttribute('role', 'button');
  147. runBtn.setAttribute('aria-label', runLabel);
  148. runBtn.appendChild(document.createTextNode('Run'));
  149. runBtn.onclick = function () {
  150. run();
  151. };
  152. tabArea.appendChild(runBtn);
  153. return tabArea;
  154. })();
  155. var editorContainer = document.createElement('div');
  156. editorContainer.className = 'editor-container';
  157. typingContainer.appendChild(tabArea);
  158. typingContainer.appendChild(editorContainer);
  159. var runContainer = document.createElement('div');
  160. runContainer.className = 'run-container';
  161. var sampleSwitcher = document.createElement('select');
  162. var sampleChapter;
  163. PLAY_SAMPLES.forEach(function (sample) {
  164. if (!sampleChapter || sampleChapter.label !== sample.chapter) {
  165. sampleChapter = document.createElement('optgroup');
  166. sampleChapter.label = sample.chapter;
  167. sampleSwitcher.appendChild(sampleChapter);
  168. }
  169. var sampleOption = document.createElement('option');
  170. sampleOption.value = sample.id;
  171. sampleOption.appendChild(document.createTextNode(sample.name));
  172. sampleChapter.appendChild(sampleOption);
  173. });
  174. sampleSwitcher.className = 'sample-switcher';
  175. var LOADED_SAMPLES = [];
  176. function findLoadedSample(sampleId) {
  177. for (var i = 0; i < LOADED_SAMPLES.length; i++) {
  178. var sample = LOADED_SAMPLES[i];
  179. if (sample.id === sampleId) {
  180. return sample;
  181. }
  182. }
  183. return null;
  184. }
  185. function findSamplePath(sampleId) {
  186. for (var i = 0; i < PLAY_SAMPLES.length; i++) {
  187. var sample = PLAY_SAMPLES[i];
  188. if (sample.id === sampleId) {
  189. return sample.path;
  190. }
  191. }
  192. return null;
  193. }
  194. function loadSample(sampleId, callback) {
  195. var sample = findLoadedSample(sampleId);
  196. if (sample) {
  197. return callback(null, sample);
  198. }
  199. var samplePath = findSamplePath(sampleId);
  200. if (!samplePath) {
  201. return callback(new Error('sample not found'));
  202. }
  203. samplePath = 'playground/new-samples/' + samplePath;
  204. var js = xhr(samplePath + '/sample.js').then(function (response) {
  205. return response.responseText;
  206. });
  207. var css = xhr(samplePath + '/sample.css').then(function (response) {
  208. return response.responseText;
  209. });
  210. var html = xhr(samplePath + '/sample.html').then(function (response) {
  211. return response.responseText;
  212. });
  213. Promise.all([js, css, html]).then(
  214. function (_) {
  215. var js = _[0];
  216. var css = _[1];
  217. var html = _[2];
  218. LOADED_SAMPLES.push({
  219. id: sampleId,
  220. js: js,
  221. css: css,
  222. html: html
  223. });
  224. return callback(null, findLoadedSample(sampleId));
  225. },
  226. function (err) {
  227. callback(err, null);
  228. }
  229. );
  230. }
  231. sampleSwitcher.onchange = function () {
  232. var sampleId = sampleSwitcher.options[sampleSwitcher.selectedIndex].value;
  233. window.location.hash = sampleId;
  234. };
  235. var playgroundContainer = document.getElementById('playground');
  236. layout();
  237. window.onresize = layout;
  238. playgroundContainer.appendChild(sampleSwitcher);
  239. playgroundContainer.appendChild(typingContainer);
  240. playgroundContainer.appendChild(runContainer);
  241. data.js.model = monaco.editor.createModel('console.log("hi")', 'javascript');
  242. data.css.model = monaco.editor.createModel('css', 'css');
  243. data.html.model = monaco.editor.createModel('html', 'html');
  244. editor = monaco.editor.create(editorContainer, {
  245. model: data.js.model,
  246. minimap: {
  247. enabled: false
  248. }
  249. });
  250. var currentToken = 0;
  251. function parseHash(firstTime) {
  252. var sampleId = window.location.hash.replace(/^#/, '');
  253. if (!sampleId) {
  254. sampleId = PLAY_SAMPLES[0].id;
  255. }
  256. if (firstTime) {
  257. for (var i = 0; i < sampleSwitcher.options.length; i++) {
  258. var opt = sampleSwitcher.options[i];
  259. if (opt.value === sampleId) {
  260. sampleSwitcher.selectedIndex = i;
  261. break;
  262. }
  263. }
  264. }
  265. var myToken = ++currentToken;
  266. loadSample(sampleId, function (err, sample) {
  267. if (err) {
  268. alert('Sample not found! ' + err.message);
  269. return;
  270. }
  271. if (myToken !== currentToken) {
  272. return;
  273. }
  274. data.js.model.setValue(sample.js);
  275. data.html.model.setValue(sample.html);
  276. data.css.model.setValue(sample.css);
  277. editor.setScrollTop(0);
  278. run();
  279. });
  280. }
  281. window.onhashchange = parseHash;
  282. parseHash(true);
  283. function run() {
  284. doRun(runContainer);
  285. }
  286. editor.addCommand(monaco.KeyMod.CtrlCmd | monaco.KeyCode.Enter, run);
  287. window.addEventListener('keydown', function keyDown(ev) {
  288. if ((isMac && !ev.metaKey) || !ev.ctrlKey) {
  289. return;
  290. }
  291. if (ev.shiftKey || ev.altKey || ev.keyCode !== 13) {
  292. return;
  293. }
  294. ev.preventDefault();
  295. run();
  296. });
  297. }
  298. var runIframe = null,
  299. runIframeHeight = 0;
  300. function doRun(runContainer) {
  301. if (runIframe) {
  302. // Unload old iframe
  303. runContainer.removeChild(runIframe);
  304. }
  305. // Load new iframe
  306. runIframe = document.createElement('iframe');
  307. runIframe.id = 'runner';
  308. runIframe.src = 'playground/playground-runner.html';
  309. runIframe.className = 'run-iframe';
  310. runIframe.style.boxSizing = 'border-box';
  311. runIframe.style.height = runIframeHeight + 'px';
  312. runIframe.style.width = '100%';
  313. runIframe.style.border = '1px solid lightgrey';
  314. runIframe.frameborder = '0';
  315. runContainer.appendChild(runIframe);
  316. var getLang = function (lang) {
  317. return data[lang].model.getValue();
  318. };
  319. runIframe.addEventListener('load', function (e) {
  320. runIframe.contentWindow.load(getLang('js'), getLang('html'), getLang('css'));
  321. });
  322. }
  323. var preloaded = {};
  324. (function () {
  325. var elements = Array.prototype.slice.call(document.querySelectorAll('pre[data-preload]'), 0);
  326. elements.forEach(function (el) {
  327. var path = el.getAttribute('data-preload');
  328. preloaded[path] = el.innerText || el.textContent;
  329. el.parentNode.removeChild(el);
  330. });
  331. })();
  332. function xhr(url) {
  333. if (preloaded[url]) {
  334. return Promise.resolve({
  335. responseText: preloaded[url]
  336. });
  337. }
  338. var req = null;
  339. return new Promise(
  340. function (c, e) {
  341. req = new XMLHttpRequest();
  342. req.onreadystatechange = function () {
  343. if (req._canceled) {
  344. return;
  345. }
  346. if (req.readyState === 4) {
  347. if ((req.status >= 200 && req.status < 300) || req.status === 1223) {
  348. c(req);
  349. } else {
  350. e(req);
  351. }
  352. req.onreadystatechange = function () {};
  353. }
  354. };
  355. req.open('GET', url, true);
  356. req.responseType = '';
  357. req.send(null);
  358. },
  359. function () {
  360. req._canceled = true;
  361. req.abort();
  362. }
  363. );
  364. }
  365. })();