From 2472cd365e2ac0df53e8333b89ac1377d9dc120c Mon Sep 17 00:00:00 2001 From: Egil Moeller Date: Sun, 12 Apr 2015 18:10:17 +0200 Subject: [PATCH] ace.js AMDification --- src/static/js/ace.js | 471 ++++++++++++++++++------------------ src/static/js/pad.js | 77 +++--- src/static/js/pad_editor.js | 46 ++-- 3 files changed, 296 insertions(+), 298 deletions(-) diff --git a/src/static/js/ace.js b/src/static/js/ace.js index 4b110f6d0..e08036fa9 100644 --- a/src/static/js/ace.js +++ b/src/static/js/ace.js @@ -24,278 +24,277 @@ // requires: plugins // requires: undefined -Ace2Editor.registry = { - nextId: 1 -}; - -var hooks = require('ep_etherpad-lite/static/js/pluginfw/hooks'); -var _ = require('./underscore'); - -function scriptTag(source) { - return ( - '' - ) -} - -function Ace2Editor() -{ - var ace2 = Ace2Editor; - - var editor = {}; - var info = { - editor: editor, - id: (ace2.registry.nextId++) +define(['ep_etherpad-lite/static/js/pluginfw/hooks', 'underscore'], function (hooks, _) { + Ace2Editor.registry = { + nextId: 1 }; - var loaded = false; - var actionsPendingInit = []; + function scriptTag(source) { + return ( + '' + ) + } - function pendingInit(func, optDoNow) + function Ace2Editor() { - return function() - { - var that = this; - var args = arguments; - var action = function() - { - func.apply(that, args); - } - if (optDoNow) - { - optDoNow.apply(that, args); - } - if (loaded) - { - action(); - } - else - { - actionsPendingInit.push(action); - } + var ace2 = Ace2Editor; + + var editor = {}; + var info = { + editor: editor, + id: (ace2.registry.nextId++) }; - } + var loaded = false; - function doActionsPendingInit() - { - _.each(actionsPendingInit, function(fn,i){ - fn() - }); - actionsPendingInit = []; - } + var actionsPendingInit = []; - ace2.registry[info.id] = info; - - // The following functions (prefixed by 'ace_') are exposed by editor, but - // execution is delayed until init is complete - var aceFunctionsPendingInit = ['importText', 'importAText', 'focus', - 'setEditable', 'getFormattedCode', 'setOnKeyPress', 'setOnKeyDown', - 'setNotifyDirty', 'setProperty', 'setBaseText', 'setBaseAttributedText', - 'applyChangesToBase', 'applyPreparedChangesetToBase', - 'setUserChangeNotificationCallback', 'setAuthorInfo', - 'setAuthorSelectionRange', 'callWithAce', 'execCommand', 'replaceRange']; - - _.each(aceFunctionsPendingInit, function(fnName,i){ - var prefix = 'ace_'; - var name = prefix + fnName; - editor[fnName] = pendingInit(function(){ - info[prefix + fnName].apply(this, arguments); - }); - }); - - editor.exportText = function() - { - if (!loaded) return "(awaiting init)\n"; - return info.ace_exportText(); - }; - - editor.getFrame = function() - { - return info.frame || null; - }; - - editor.getDebugProperty = function(prop) - { - return info.ace_getDebugProperty(prop); - }; - - editor.getInInternationalComposition = function() - { - if (!loaded) return false; - return info.ace_getInInternationalComposition(); - }; - - // prepareUserChangeset: - // Returns null if no new changes or ACE not ready. Otherwise, bundles up all user changes - // to the latest base text into a Changeset, which is returned (as a string if encodeAsString). - // If this method returns a truthy value, then applyPreparedChangesetToBase can be called - // at some later point to consider these changes part of the base, after which prepareUserChangeset - // must be called again before applyPreparedChangesetToBase. Multiple consecutive calls - // to prepareUserChangeset will return an updated changeset that takes into account the - // latest user changes, and modify the changeset to be applied by applyPreparedChangesetToBase - // accordingly. - editor.prepareUserChangeset = function() - { - if (!loaded) return null; - return info.ace_prepareUserChangeset(); - }; - - editor.getUnhandledErrors = function() - { - if (!loaded) return []; - // returns array of {error: , time: +new Date()} - return info.ace_getUnhandledErrors(); - }; - - function pushStyleTagsFor(buffer, files) { - for (var i = 0, ii = files.length; i < ii; i++) { - var file = files[i]; - buffer.push(''); + function pendingInit(func, optDoNow) + { + return function() + { + var that = this; + var args = arguments; + var action = function() + { + func.apply(that, args); + } + if (optDoNow) + { + optDoNow.apply(that, args); + } + if (loaded) + { + action(); + } + else + { + actionsPendingInit.push(action); + } + }; } - } - editor.destroy = pendingInit(function() - { - info.ace_dispose(); - info.frame.parentNode.removeChild(info.frame); - delete ace2.registry[info.id]; - info = null; // prevent IE 6 closure memory leaks - }); - - editor.init = function(containerId, initialCode, doneFunc) - { - - editor.importText(initialCode); - - info.onEditorReady = function() + function doActionsPendingInit() { - loaded = true; - doActionsPendingInit(); - doneFunc(); + _.each(actionsPendingInit, function(fn,i){ + fn() + }); + actionsPendingInit = []; + } + + ace2.registry[info.id] = info; + + // The following functions (prefixed by 'ace_') are exposed by editor, but + // execution is delayed until init is complete + var aceFunctionsPendingInit = ['importText', 'importAText', 'focus', + 'setEditable', 'getFormattedCode', 'setOnKeyPress', 'setOnKeyDown', + 'setNotifyDirty', 'setProperty', 'setBaseText', 'setBaseAttributedText', + 'applyChangesToBase', 'applyPreparedChangesetToBase', + 'setUserChangeNotificationCallback', 'setAuthorInfo', + 'setAuthorSelectionRange', 'callWithAce', 'execCommand', 'replaceRange']; + + _.each(aceFunctionsPendingInit, function(fnName,i){ + var prefix = 'ace_'; + var name = prefix + fnName; + editor[fnName] = pendingInit(function(){ + info[prefix + fnName].apply(this, arguments); + }); + }); + + editor.exportText = function() + { + if (!loaded) return "(awaiting init)\n"; + return info.ace_exportText(); }; - (function() + editor.getFrame = function() { - var doctype = ""; + return info.frame || null; + }; - var iframeHTML = []; + editor.getDebugProperty = function(prop) + { + return info.ace_getDebugProperty(prop); + }; - iframeHTML.push(doctype); - iframeHTML.push(""); + editor.getInInternationalComposition = function() + { + if (!loaded) return false; + return info.ace_getInInternationalComposition(); + }; - var includedCSS = ["../static/css/iframe_editor.css", - "../static/css/pad.css", - "../static/custom/pad.css"] - - var additionalCSS = _(hooks.callAll("aceEditorCSS")).map(function(path){ return '../static/plugins/' + path }); - includedCSS = includedCSS.concat(additionalCSS); + // prepareUserChangeset: + // Returns null if no new changes or ACE not ready. Otherwise, bundles up all user changes + // to the latest base text into a Changeset, which is returned (as a string if encodeAsString). + // If this method returns a truthy value, then applyPreparedChangesetToBase can be called + // at some later point to consider these changes part of the base, after which prepareUserChangeset + // must be called again before applyPreparedChangesetToBase. Multiple consecutive calls + // to prepareUserChangeset will return an updated changeset that takes into account the + // latest user changes, and modify the changeset to be applied by applyPreparedChangesetToBase + // accordingly. + editor.prepareUserChangeset = function() + { + if (!loaded) return null; + return info.ace_prepareUserChangeset(); + }; - pushStyleTagsFor(iframeHTML, includedCSS); + editor.getUnhandledErrors = function() + { + if (!loaded) return []; + // returns array of {error: , time: +new Date()} + return info.ace_getUnhandledErrors(); + }; - iframeHTML.push(''); - iframeHTML.push(scriptTag('\n\ - require.setRootURI("../javascripts/src");\n\ - require.setLibraryURI("../javascripts/lib");\n\ - require.setGlobalKeyPath("require");\n\ - ')); + function pushStyleTagsFor(buffer, files) { + for (var i = 0, ii = files.length; i < ii; i++) { + var file = files[i]; + buffer.push(''); + } + } - iframeHTML.push(''); + editor.destroy = pendingInit(function() + { + info.ace_dispose(); + info.frame.parentNode.removeChild(info.frame); + delete ace2.registry[info.id]; + info = null; // prevent IE 6 closure memory leaks + }); - iframeHTML.push(scriptTag('\n\ - var pathComponents = parent.parent.location.pathname.split("/");\n\ - var baseURL = pathComponents.slice(0,pathComponents.length-2).join("/") + "/";\n\ - requirejs.config({\n\ - baseUrl: baseURL + "static/plugins",\n\ - paths: {underscore: baseURL + "static/plugins/underscore/underscore"}\n\ - });\n\ - \n\ - requirejs(["ep_etherpad-lite/static/js/rjquery", "ep_etherpad-lite/static/js/pluginfw/client_plugins", "ep_etherpad-lite/static/js/ace2_inner"], function (j, plugins, Ace2Inner) {\n\ - jQuery = $ = window.jQuery = window.$ = j; // Expose jQuery #HACK\n\ + editor.init = function(containerId, initialCode, doneFunc) + { + + editor.importText(initialCode); + + info.onEditorReady = function() + { + loaded = true; + doActionsPendingInit(); + doneFunc(); + }; + + (function() + { + var doctype = ""; + + var iframeHTML = []; + + iframeHTML.push(doctype); + iframeHTML.push(""); + + var includedCSS = ["../static/css/iframe_editor.css", + "../static/css/pad.css", + "../static/custom/pad.css"] + + var additionalCSS = _(hooks.callAll("aceEditorCSS")).map(function(path){ return '../static/plugins/' + path }); + includedCSS = includedCSS.concat(additionalCSS); + + pushStyleTagsFor(iframeHTML, includedCSS); + + iframeHTML.push(''); + iframeHTML.push(scriptTag('\n\ + require.setRootURI("../javascripts/src");\n\ + require.setLibraryURI("../javascripts/lib");\n\ + require.setGlobalKeyPath("require");\n\ + ')); + + iframeHTML.push(''); + + iframeHTML.push(scriptTag('\n\ + var pathComponents = parent.parent.location.pathname.split("/");\n\ + var baseURL = pathComponents.slice(0,pathComponents.length-2).join("/") + "/";\n\ + requirejs.config({\n\ + baseUrl: baseURL + "static/plugins",\n\ + paths: {underscore: baseURL + "static/plugins/underscore/underscore"}\n\ + });\n\ \n\ - plugins.adoptPluginsFromAncestorsOf(window, function () {\n\ - var hooks = require("ep_etherpad-lite/static/js/pluginfw/hooks");\n\ - hooks.plugins = plugins;\n\ + requirejs(["ep_etherpad-lite/static/js/rjquery", "ep_etherpad-lite/static/js/pluginfw/client_plugins", "ep_etherpad-lite/static/js/ace2_inner"], function (j, plugins, Ace2Inner) {\n\ + jQuery = $ = window.jQuery = window.$ = j; // Expose jQuery #HACK\n\ \n\ - plugins.ensure(function () {\n\ - Ace2Inner.init();\n\ + plugins.adoptPluginsFromAncestorsOf(window, function () {\n\ + var hooks = require("ep_etherpad-lite/static/js/pluginfw/hooks");\n\ + hooks.plugins = plugins;\n\ + \n\ + plugins.ensure(function () {\n\ + Ace2Inner.init();\n\ + });\n\ });\n\ });\n\ - });\n\ - ')); + ')); - iframeHTML.push(''); + iframeHTML.push(''); - hooks.callAll("aceInitInnerdocbodyHead", { - iframeHTML: iframeHTML - }); + hooks.callAll("aceInitInnerdocbodyHead", { + iframeHTML: iframeHTML + }); - iframeHTML.push(' '); + iframeHTML.push(' '); - // Expose myself to global for my child frame. - var thisFunctionsName = "ChildAccessibleAce2Editor"; - (function () {return this}())[thisFunctionsName] = Ace2Editor; + // Expose myself to global for my child frame. + var thisFunctionsName = "ChildAccessibleAce2Editor"; + (function () {return this}())[thisFunctionsName] = Ace2Editor; - var outerScript = '\ -editorId = ' + JSON.stringify(info.id) + ';\n\ -editorInfo = parent[' + JSON.stringify(thisFunctionsName) + '].registry[editorId];\n\ -window.onload = function () {\n\ - window.onload = null;\n\ - setTimeout(function () {\n\ - var iframe = document.createElement("IFRAME");\n\ - iframe.name = "ace_inner";\n\ - iframe.title = "pad";\n\ - iframe.scrolling = "no";\n\ - var outerdocbody = document.getElementById("outerdocbody");\n\ - iframe.frameBorder = 0;\n\ - iframe.allowTransparency = true; // for IE\n\ - outerdocbody.insertBefore(iframe, outerdocbody.firstChild);\n\ - iframe.ace_outerWin = window;\n\ - readyFunc = function () {\n\ - editorInfo.onEditorReady();\n\ - readyFunc = null;\n\ - editorInfo = null;\n\ - };\n\ - var doc = iframe.contentWindow.document;\n\ - doc.open();\n\ - var text = (' + JSON.stringify(iframeHTML.join('\n')) + ');\n\ - doc.write(text);\n\ - doc.close();\n\ - }, 0);\n\ -}'; + var outerScript = '\ + editorId = ' + JSON.stringify(info.id) + ';\n\ + editorInfo = parent[' + JSON.stringify(thisFunctionsName) + '].registry[editorId];\n\ + window.onload = function () {\n\ + window.onload = null;\n\ + setTimeout(function () {\n\ + var iframe = document.createElement("IFRAME");\n\ + iframe.name = "ace_inner";\n\ + iframe.title = "pad";\n\ + iframe.scrolling = "no";\n\ + var outerdocbody = document.getElementById("outerdocbody");\n\ + iframe.frameBorder = 0;\n\ + iframe.allowTransparency = true; // for IE\n\ + outerdocbody.insertBefore(iframe, outerdocbody.firstChild);\n\ + iframe.ace_outerWin = window;\n\ + readyFunc = function () {\n\ + editorInfo.onEditorReady();\n\ + readyFunc = null;\n\ + editorInfo = null;\n\ + };\n\ + var doc = iframe.contentWindow.document;\n\ + doc.open();\n\ + var text = (' + JSON.stringify(iframeHTML.join('\n')) + ');\n\ + doc.write(text);\n\ + doc.close();\n\ + }, 0);\n\ + }'; - var outerHTML = [doctype, ''] + var outerHTML = [doctype, ''] - var includedCSS = [ - "../static/css/iframe_editor.css", - "../static/css/pad.css", - "../static/custom/pad.css"]; + var includedCSS = [ + "../static/css/iframe_editor.css", + "../static/css/pad.css", + "../static/custom/pad.css"]; - var additionalCSS = _(hooks.callAll("aceEditorCSS")).map(function(path){ return '../static/plugins/' + path }); - includedCSS = includedCSS.concat(additionalCSS); + var additionalCSS = _(hooks.callAll("aceEditorCSS")).map(function(path){ return '../static/plugins/' + path }); + includedCSS = includedCSS.concat(additionalCSS); - pushStyleTagsFor(outerHTML, includedCSS); + pushStyleTagsFor(outerHTML, includedCSS); - // bizarrely, in FF2, a file with no "external" dependencies won't finish loading properly - // (throbs busy while typing) - outerHTML.push('', '', scriptTag(outerScript), '
x
'); + // bizarrely, in FF2, a file with no "external" dependencies won't finish loading properly + // (throbs busy while typing) + outerHTML.push('', '', scriptTag(outerScript), '
x
'); - var outerFrame = document.createElement("IFRAME"); - outerFrame.name = "ace_outer"; - outerFrame.frameBorder = 0; // for IE - outerFrame.title = "Ether"; - info.frame = outerFrame; - document.getElementById(containerId).appendChild(outerFrame); + var outerFrame = document.createElement("IFRAME"); + outerFrame.name = "ace_outer"; + outerFrame.frameBorder = 0; // for IE + outerFrame.title = "Ether"; + info.frame = outerFrame; + document.getElementById(containerId).appendChild(outerFrame); - var editorDocument = outerFrame.contentWindow.document; + var editorDocument = outerFrame.contentWindow.document; - editorDocument.open(); - editorDocument.write(outerHTML.join('')); - editorDocument.close(); - })(); - }; + editorDocument.open(); + editorDocument.write(outerHTML.join('')); + editorDocument.close(); + })(); + }; - return editor; -} + return editor; + } -exports.Ace2Editor = Ace2Editor; + return {Ace2Editor: Ace2Editor}; +}); diff --git a/src/static/js/pad.js b/src/static/js/pad.js index 362a4b1ad..52219fe50 100644 --- a/src/static/js/pad.js +++ b/src/static/js/pad.js @@ -474,7 +474,10 @@ var pad = { // start the custom js if (typeof customStart == "function") customStart(); getParams(); - handshake(); + + padeditor.init(function () { + handshake(); + }, pad.padOptions.view || {}, pad); // To use etherpad you have to allow cookies. // This will check if the creation of a test-cookie has success. @@ -525,12 +528,9 @@ var pad = { padimpexp.init(this); padsavedrevs.init(this); - padeditor.init(postAceInit, pad.padOptions.view || {}, this); - paduserlist.init(pad.myUserInfo, this); padconnectionstatus.init(); padmodals.init(this); - pad.collabClient = getCollabClient(padeditor.ace, clientVars.collab_client_vars, pad.myUserInfo, { colorPalette: pad.getColorPalette() }, pad); @@ -554,44 +554,41 @@ var pad = { $("#chatloadmessagesbutton").css("display", "none"); } - function postAceInit() + padeditbar.init(); + setTimeout(function() { - padeditbar.init(); - setTimeout(function() - { - padeditor.ace.focus(); - }, 0); - if(padcookie.getPref("chatAlwaysVisible")){ // if we have a cookie for always showing chat then show it - chat.stickToScreen(true); // stick it to the screen - $('#options-stickychat').prop("checked", true); // set the checkbox to on - } - if(padcookie.getPref("chatAndUsers")){ // if we have a cookie for always showing chat then show it - chat.chatAndUsers(true); // stick it to the screen - $('#options-chatandusers').prop("checked", true); // set the checkbox to on - } - if(padcookie.getPref("showAuthorshipColors") == false){ - pad.changeViewOption('showAuthorColors', false); - } - if(padcookie.getPref("showLineNumbers") == false){ - pad.changeViewOption('showLineNumbers', false); - } - if(padcookie.getPref("rtlIsTrue") == true){ - pad.changeViewOption('rtlIsTrue', true); - } - - var fonts = ['useMonospaceFont', 'useOpenDyslexicFont', 'useComicSansFont', 'useCourierNewFont', 'useGeorgiaFont', 'useImpactFont', - 'useLucidaFont', 'useLucidaSansFont', 'usePalatinoFont', 'useTahomaFont', 'useTimesNewRomanFont', - 'useTrebuchetFont', 'useVerdanaFont', 'useSymbolFont', 'useWebdingsFont', 'useWingDingsFont', 'useSansSerifFont', - 'useSerifFont']; - - $.each(fonts, function(i, font){ - if(padcookie.getPref(font) == true){ - pad.changeViewOption(font, true); - } - }) - - hooks.aCallAll("postAceInit", {ace: padeditor.ace, pad: pad}); + padeditor.ace.focus(); + }, 0); + if(padcookie.getPref("chatAlwaysVisible")){ // if we have a cookie for always showing chat then show it + chat.stickToScreen(true); // stick it to the screen + $('#options-stickychat').prop("checked", true); // set the checkbox to on } + if(padcookie.getPref("chatAndUsers")){ // if we have a cookie for always showing chat then show it + chat.chatAndUsers(true); // stick it to the screen + $('#options-chatandusers').prop("checked", true); // set the checkbox to on + } + if(padcookie.getPref("showAuthorshipColors") == false){ + pad.changeViewOption('showAuthorColors', false); + } + if(padcookie.getPref("showLineNumbers") == false){ + pad.changeViewOption('showLineNumbers', false); + } + if(padcookie.getPref("rtlIsTrue") == true){ + pad.changeViewOption('rtlIsTrue', true); + } + + var fonts = ['useMonospaceFont', 'useOpenDyslexicFont', 'useComicSansFont', 'useCourierNewFont', 'useGeorgiaFont', 'useImpactFont', + 'useLucidaFont', 'useLucidaSansFont', 'usePalatinoFont', 'useTahomaFont', 'useTimesNewRomanFont', + 'useTrebuchetFont', 'useVerdanaFont', 'useSymbolFont', 'useWebdingsFont', 'useWingDingsFont', 'useSansSerifFont', + 'useSerifFont']; + + $.each(fonts, function(i, font){ + if(padcookie.getPref(font) == true){ + pad.changeViewOption(font, true); + } + }) + + hooks.aCallAll("postAceInit", {ace: padeditor.ace, pad: pad}); }, dispose: function() { diff --git a/src/static/js/pad_editor.js b/src/static/js/pad_editor.js index b1ea09f72..dc1435d33 100644 --- a/src/static/js/pad_editor.js +++ b/src/static/js/pad_editor.js @@ -39,33 +39,35 @@ var padeditor = (function() ace: null, // this is accessed directly from other files viewZoom: 100, - init: function(readyFunc, initialViewOptions, _pad) - { - Ace2Editor = require('./ace').Ace2Editor; - pad = _pad; - settings = pad.settings; + init: function(readyFunc, initialViewOptions, _pad) { + requirejs(['ep_etherpad-lite/static/js/ace'], function (ace) { - function aceReady() - { - $("#editorloadingbox").hide(); - if (readyFunc) + Ace2Editor = ace.Ace2Editor; + pad = _pad; + settings = pad.settings; + + function aceReady() { - readyFunc(); + $("#editorloadingbox").hide(); + if (readyFunc) + { + readyFunc(); + } } - } - self.ace = new Ace2Editor(); - self.ace.init("editorcontainer", "", aceReady); - self.ace.setProperty("wraps", true); - if (pad.getIsDebugEnabled()) - { - self.ace.setProperty("dmesg", pad.dmesg); - } - self.initViewOptions(); - self.setViewOptions(initialViewOptions); + self.ace = new Ace2Editor(); + self.ace.init("editorcontainer", "", aceReady); + self.ace.setProperty("wraps", true); + if (pad.getIsDebugEnabled()) + { + self.ace.setProperty("dmesg", pad.dmesg); + } + self.initViewOptions(); + self.setViewOptions(initialViewOptions); - // view bar - $("#viewbarcontents").show(); + // view bar + $("#viewbarcontents").show(); + }); }, initViewOptions: function() {