GitHub PullRequestReviewCommentEvent at 2014-06-19 06:46:21
Repository: WebODF
Actor: kossebau
Payload: {"action":"created","comment":{"url":"https://api.github.com/repos/kogmbh/WebODF/pulls/comments/13955292","id":13955292,"diff_hunk":"@@ -0,0 +1,714 @@\n+/**\n+ * Copyright (C) 2014 KO GmbH <copyright@kogmbh.com>\n+ *\n+ * @licstart\n+ * This file is part of WebODF.\n+ *\n+ * WebODF is free software: you can redistribute it and/or modify it\n+ * under the terms of the GNU Affero General Public License (GNU AGPL)\n+ * as published by the Free Software Foundation, either version 3 of\n+ * the License, or (at your option) any later version.\n+ *\n+ * WebODF is distributed in the hope that it will be useful, but\n+ * WITHOUT ANY WARRANTY; without even the implied warranty of\n+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n+ * GNU Affero General Public License for more details.\n+ *\n+ * You should have received a copy of the GNU Affero General Public License\n+ * along with WebODF.  If not, see <http://www.gnu.org/licenses/>.\n+ * @licend\n+ *\n+ * @source: http://www.webodf.org/\n+ * @source: https://github.com/kogmbh/WebODF/\n+ */\n+\n+/**\n+ * Namespace of the Wodo.TextEditor\n+ * @namespace\n+ */\n+var Wodo = Wodo || (function () {\n+    var installationPath = (function() {\n+        var path = \".\",\n+            a, pathname, pos;\n+\n+        if (document.currentScript && document.currentScript.src) {\n+            a = document.createElement('a');\n+            a.href = document.currentScript.src;\n+            pathname = a.pathname;\n+\n+            pos = pathname.lastIndexOf(\"/\");\n+            if (pos !== -1) {\n+                path = pathname.substr(0, pos);\n+            }\n+        }\n+        return path;\n+    }());\n+\n+    dojoConfig = (function() {\n+        var WebODFEditorDojoLocale = \"C\";\n+\n+        if (navigator && navigator.language.match(/^(de)/)) {\n+            WebODFEditorDojoLocale = navigator.language.substr(0, 2);\n+        }\n+\n+        return {\n+            locale: WebODFEditorDojoLocale,\n+            paths: {\n+                \"webodf/editor\": installationPath,\n+                \"dijit\":         installationPath + \"/dijit\",\n+                \"dojox\":         installationPath + \"/dojox\",\n+                \"dojo\":          installationPath + \"/dojo\",\n+                \"resources\":     installationPath + \"/resources\"\n+            }\n+        };\n+    }());\n+\n+\n+    \"use strict\";\n+\n+    var /** @inner @type{!boolean} */\n+        isInitalized = false,\n+        /** @inner @type{!Array.<!function():undefined>} */\n+        pendingInstanceCreationCalls = [],\n+        /** @inner @type{!number} */\n+        instanceCounter = 0,\n+        // TODO: avatar image url needs base-url setting.\n+        // so far Wodo itself does not have a setup call,\n+        // but then the avatar is also not used yet here\n+        defaultUserData = {\n+            fullName: \"\",\n+            color:    \"black\",\n+            imageUrl: \"avatar-joe.png\"\n+        },\n+        /** @inner @const\n+            @type{!Array.<!string>} */\n+        userDataFieldNames = [\"fullName\", \"color\", \"imageUrl\"],\n+        /** @inner @const\n+            @type{!string} */\n+        memberId = \"localuser\",\n+        // constructors\n+        BorderContainer, ContentPane, FullWindowZoomHelper, EditorSession, Tools,\n+        /** @inner @const\n+            @type{!string} */\n+        EVENT_UNKNOWNERROR = \"unknownError\",\n+        /** @inner @const\n+            @type {!string} */\n+        EVENT_METADATACHANGED = \"metadataChanged\";\n+\n+\n+    /**\n+     * @return {undefined}\n+     */\n+    function initTextEditor() {\n+        require({ }, [\n+            \"dijit/layout/BorderContainer\",\n+            \"dijit/layout/ContentPane\",\n+            \"webodf/editor/FullWindowZoomHelper\",\n+            \"webodf/editor/EditorSession\",\n+            \"webodf/editor/Tools\",\n+            \"webodf/editor/Translator\"],\n+            function (BC, CP, FWZH, ES, T, Translator) {\n+                var locale = navigator.language || \"en-US\",\n+                    editorBase = dojo.config && dojo.config.paths && dojo.config.paths[\"webodf/editor\"],\n+                    translationsDir = editorBase + '/translations',\n+                    t;\n+\n+                BorderContainer = BC;\n+                ContentPane = CP;\n+                FullWindowZoomHelper = FWZH,\n+                EditorSession = ES;\n+                Tools = T;\n+\n+                // TODO: locale cannot be set by the user, also different for different editors\n+                t = new Translator(translationsDir, locale, function (editorTranslator) {\n+                    runtime.setTranslator(editorTranslator.translate);\n+                    // Extend runtime with a convenient translation function\n+                    runtime.translateContent = function (node) {\n+                        var i,\n+                            element,\n+                            tag,\n+                            placeholder,\n+                            translatable = node.querySelectorAll(\"*[text-i18n]\");\n+\n+                        for (i = 0; i < translatable.length; i += 1) {\n+                            element = translatable[i];\n+                            tag = element.localName;\n+                            placeholder = element.getAttribute('text-i18n');\n+                            if (tag === \"label\"\n+                                    || tag === \"span\"\n+                                    || /h\\d/i.test(tag)) {\n+                                element.textContent = runtime.tr(placeholder);\n+                            }\n+                        }\n+                    };\n+\n+                    defaultUserData.fullName = runtime.tr(\"Unknown Author\");\n+\n+                    isInitalized = true;\n+                    pendingInstanceCreationCalls.forEach(function (create) { create(); });\n+                });\n+            }\n+        );\n+    }\n+\n+    /**\n+     * Creates a new record with userdata, and for all official fields\n+     * copies over the value from the original or, if not present there,\n+     * sets it to the default value.\n+     * @param {?Object.<!string,!string>|undefined} original, defaults to {}\n+     * @return {!Object.<!string,!string>}\n+     */\n+    function cloneUserData(original) {\n+        var result = {};\n+\n+        if (!original) {\n+            original = {};\n+        }\n+\n+        userDataFieldNames.forEach(function (fieldName) {\n+            result[fieldName] = original[fieldName] || defaultUserData[fieldName];\n+        });\n+\n+        return result;\n+    }\n+\n+    /**\n+     * @name TextEditor\n+     * @constructor\n+     * @param {!string} mainContainerElementId\n+     * @param {!Object.<!string,!*>} editorOptions\n+     */\n+    function TextEditor(mainContainerElementId, editorOptions) {\n+        instanceCounter = instanceCounter + 1;\n+\n+        /**\n+        * Returns true if either all features are wanted and this one is not explicitely disabled\n+        * or if not all features are wanted by default and it is explicitely enabled\n+        * @param {?boolean|undefined} isFeatureEnabled explicit flag which enables a feature\n+        * @return {!boolean}\n+        */\n+        function isEnabled(isFeatureEnabled) {\n+            return editorOptions.allFeaturesEnabled ? (isFeatureEnabled !== false) : isFeatureEnabled;\n+        }\n+\n+        var self = this,\n+            userData,\n+            //\n+            mainContainerElement = document.getElementById(mainContainerElementId),\n+            canvasElement,\n+            canvasContainerElement,\n+            toolbarElement,\n+            toolbarContainerElement, // needed because dijit toolbar overwrites direct classList\n+            editorElement,\n+            /** @inner @const\n+                @type{!string} */\n+            canvasElementId = \"webodfeditor-canvas\" + instanceCounter,\n+            /** @inner @const\n+                @type{!string} */\n+            canvasContainerElementId = \"webodfeditor-canvascontainer\" + instanceCounter,\n+            /** @inner @const\n+                @type{!string} */\n+            toolbarElementId = \"webodfeditor-toolbar\" + instanceCounter,\n+            /** @inner @const\n+                @type{!string} */\n+            editorElementId = \"webodfeditor-editor\" + instanceCounter,\n+            //\n+            fullWindowZoomHelper,\n+            //\n+            mainContainer,\n+            tools,\n+            odfCanvas,\n+            //\n+            editorSession,\n+            session,\n+            //\n+            loadOdtFile = editorOptions.loadCallback,\n+            saveOdtFile = editorOptions.saveCallback,\n+            close =       editorOptions.closeCallback,\n+            //\n+            directTextStylingEnabled = isEnabled(editorOptions.directTextStylingEnabled),\n+            directParagraphStylingEnabled = isEnabled(editorOptions.directParagraphStylingEnabled),\n+            paragraphStyleSelectingEnabled = isEnabled(editorOptions.paragraphStyleSelectingEnabled),\n+            paragraphStyleEditingEnabled = isEnabled(editorOptions.paragraphStyleEditingEnabled),\n+            imageEditingEnabled = isEnabled(editorOptions.imageEditingEnabled),\n+            hyperlinkEditingEnabled = isEnabled(editorOptions.hyperlinkEditingEnabled),\n+            reviewModeEnabled = isEnabled(editorOptions.reviewModeEnabled),\n+            annotationsEnabled = reviewModeEnabled || isEnabled(editorOptions.annotationsEnabled),\n+            undoRedoEnabled = isEnabled(editorOptions.undoRedoEnabled),\n+            zoomingEnabled = isEnabled(editorOptions.zoomingEnabled),\n+            //\n+            pendingMemberId,\n+            pendingEditorReadyCallback,\n+            //\n+            eventNotifier = new core.EventNotifier([\n+                EVENT_UNKNOWNERROR,\n+                EVENT_METADATACHANGED\n+            ]);\n+\n+        runtime.assert(Boolean(mainContainerElement), \"No id of an existing element passed to Wodo.createTextEditor(): \"+mainContainerElementId);\n+\n+        /**\n+         * @param {!Object} changes\n+         * @return {undefined}\n+         */\n+        function relayMetadataSignal(changes) {\n+            eventNotifier.emit(EVENT_METADATACHANGED, changes);\n+        }\n+\n+        /**\n+         * @return {undefined}\n+         */\n+        function createSession() {\n+            var viewOptions = {\n+                    editInfoMarkersInitiallyVisible: false,\n+                    caretAvatarsInitiallyVisible: false,\n+                    caretBlinksOnRangeSelect: true\n+                };\n+\n+            // create session around loaded document\n+            session = new ops.Session(odfCanvas);\n+            editorSession = new EditorSession(session, pendingMemberId, {\n+                viewOptions: viewOptions,\n+                directTextStylingEnabled: directTextStylingEnabled,\n+                directParagraphStylingEnabled: directParagraphStylingEnabled,\n+                paragraphStyleSelectingEnabled: paragraphStyleSelectingEnabled,\n+                paragraphStyleEditingEnabled: paragraphStyleEditingEnabled,\n+                imageEditingEnabled: imageEditingEnabled,\n+                hyperlinkEditingEnabled: hyperlinkEditingEnabled,\n+                annotationsEnabled: annotationsEnabled,\n+                zoomingEnabled: zoomingEnabled,\n+                reviewModeEnabled: reviewModeEnabled\n+            });\n+            if (undoRedoEnabled) {\n+                editorSession.sessionController.setUndoManager(new gui.TrivialUndoManager());\n+            }\n+\n+            // Relay any metadata changes to the Editor's consumer as an event\n+            editorSession.sessionController.getMetadataController().subscribe(gui.MetadataController.signalMetadataChanged, relayMetadataSignal);\n+\n+            // and report back to caller\n+            pendingEditorReadyCallback();\n+            // reset\n+            pendingEditorReadyCallback = null;\n+            pendingMemberId = null;\n+        }\n+\n+        /**\n+         * @return {undefined}\n+         */\n+        function startEditing() {\n+            runtime.assert(editorSession, \"editorSession should exist here.\");\n+\n+            tools.setEditorSession(editorSession);\n+            editorSession.sessionController.insertLocalCursor();\n+            editorSession.sessionController.startEditing();\n+        }\n+\n+        /**\n+         * @return {undefined}\n+         */\n+        function endEditing() {\n+            runtime.assert(editorSession, \"editorSession should exist here.\");\n+\n+            tools.setEditorSession(undefined);\n+            editorSession.sessionController.endEditing();\n+            editorSession.sessionController.removeLocalCursor();\n+        }\n+\n+        /**\n+         * Loads an ODT document into the editor.\n+         * @name TextEditor#openDocumentFromUrl\n+         * @function\n+         * @param {!string} docUrl url from which the ODT document can be loaded\n+         * @param {!function(!Error=):undefined} callback Called once the document has been opened, passes an error object in case of error\n+         * @return {undefined}\n+         */\n+        this.openDocumentFromUrl = function(docUrl, editorReadyCallback) {\n+            runtime.assert(docUrl, \"document should be defined here.\");\n+            runtime.assert(!pendingEditorReadyCallback, \"pendingEditorReadyCallback should not exist here.\");\n+            runtime.assert(!editorSession, \"editorSession should not exist here.\");\n+            runtime.assert(!session, \"session should not exist here.\");\n+\n+            pendingMemberId = memberId;\n+            pendingEditorReadyCallback = function () {\n+                var op = new ops.OpAddMember();\n+                op.init({\n+                    memberid: memberId,\n+                    setProperties: userData\n+                });\n+                session.enqueue([op]);\n+                startEditing();\n+                if (editorReadyCallback) {\n+                    editorReadyCallback();\n+                }\n+            };\n+\n+            odfCanvas.load(docUrl);\n+        }\n+\n+        /**\n+         * Closes the document, and does cleanup.\n+         * @name TextEditor#closeDocument\n+         * @function\n+         * @param {!function(!Error=):undefined} callback  Called once the document has been closed, passes an error object in case of error\n+         * @return {undefined}\n+         */\n+        this.closeDocument = function(callback) {\n+            runtime.assert(session, \"session should exist here.\");\n+\n+            endEditing();\n+\n+            var op = new ops.OpRemoveMember();\n+            op.init({\n+                memberid: memberId\n+            });\n+            session.enqueue([op]);\n+\n+            session.close(function (err) {\n+                if (err) {\n+                    callback(err);\n+                } else {\n+                    editorSession.sessionController.getMetadataController().unsubscribe(gui.MetadataController.signalMetadataChanged, relayMetadataSignal);\n+                    editorSession.destroy(function (err) {\n+                        if (err) {\n+                            callback(err);\n+                        } else {\n+                            editorSession = undefined;\n+                            session.destroy(function (err) {\n+                                if (err) {\n+                                    callback(err);\n+                                } else {\n+                                    session = undefined;\n+                                    callback();\n+                                }\n+                            });\n+                        }\n+                    });\n+                }\n+            });\n+        }\n+\n+        /**\n+         * @name TextEditor#getDocumentAsByteArray\n+         * @function\n+         * @param {!function(err:?Error, file:!Uint8Array=):undefined} callback Called with the current document as ODT file as bytearray, passes an error object in case of error\n+         * @return {undefined}\n+         */\n+        this.getDocumentAsByteArray = function(callback) {\n+            var odfContainer = odfCanvas.odfContainer();\n+\n+            if (odfContainer) {\n+                odfContainer.createByteArray(function(ba) {\n+                    callback(null, ba);\n+                }, function(errorString) {\n+                    callback(new Error(errorString ? errorString : \"Could not create bytearray from OdfContainer.\"));\n+                });\n+            } else {\n+                callback(new Error(\"No odfContainer set!\"));\n+            }\n+        }\n+\n+        /**\n+         * Sets the metadata fields from the given properties map.\n+         * Avoid setting certain fields since they are automatically set:\n+         *    dc:creator\n+         *    dc:date\n+         *    meta:editing-cycles\n+         *\n+         * The following properties are never used and will be removed for semantic\n+         * consistency from the document:\n+         *     meta:editing-duration\n+         *     meta:document-statistic\n+         *\n+         * Setting any of the above mentioned fields using this method will have no effect.\n+         *\n+         * @name TextEditor#setMetadata\n+         * @function\n+         * @param {?Object.<!string, !string>} setProperties A flat object that is a string->string map of field name -> value.\n+         * @param {?Array.<!string>} removedProperties An array of metadata field names (prefixed).\n+         * @return {undefined}\n+         */\n+        this.setMetadata = function(setProperties, removedProperties) {\n+            runtime.assert(editorSession, \"editorSession should exist here.\");\n+\n+            editorSession.sessionController.getMetadataController().setMetadata(setProperties, removedProperties);\n+        };\n+\n+        /**\n+         * Returns the value of the requested document metadata field.\n+         * @name TextEditor#getMetadata\n+         * @function\n+         * @param {!string} property A namespace-prefixed field name, for example\n+         * dc:creator\n+         * @return {?string}\n+         */\n+        this.getMetadata = function(property) {\n+            runtime.assert(editorSession, \"editorSession should exist here.\");\n+\n+            return editorSession.sessionController.getMetadataController().getMetadata(property);\n+        };\n+\n+        /**\n+         * Sets the data for the person that is editing the document.\n+         * The supported fields are:\n+         *     \"fullName\": the full name of the editing person\n+         *     \"color\": color to use for the user specific UI elements\n+         * @name TextEditor#setUserData\n+         * @function\n+         * @param {?Object.<!string,!string>|undefined} data\n+         * @return {undefined}\n+         */\n+        function setUserData(data) {\n+            userData = cloneUserData(data);\n+        }\n+        this.setUserData = setUserData;\n+\n+        /**\n+         * Returns the data set for the person that is editing the document.\n+         * @name TextEditor#getUserData\n+         * @function\n+         * @return {!Object.<!string,!string>}\n+         */\n+        this.getUserData = function() {\n+            return cloneUserData(userData);\n+        }\n+\n+        /**\n+         * @return {undefined}\n+         */\n+        function setFocusToOdfCanvas() {\n+            editorSession.sessionController.getEventManager().focus();\n+        }\n+\n+        /**\n+         * @param {!function(!Error=):undefined} callback passes an error object in case of error\n+         * @return {undefined}\n+         */\n+        function destroyInternal(callback) {\n+            mainContainerElement.removeChild(editorElement);\n+\n+            callback();\n+        }\n+\n+        /**\n+         * Destructs the editor object completely.\n+         * @name TextEditor#destroy\n+         * @function\n+         * @param {!function(!Error=):undefined} callback Called once the destruction has been completed, passes an error object in case of error\n+         * @return {undefined}\n+         */\n+        this.destroy = function(callback) {\n+            var destroyCallbacks = [];\n+\n+            // TODO: decide if some forced close should be done here instead of enforcing proper API usage\n+            runtime.assert(!session, \"session should not exist here.\");\n+\n+            // TODO: investigate what else needs to be done\n+            mainContainer.destroyRecursive(true);\n+\n+            destroyCallbacks = destroyCallbacks.concat([\n+                fullWindowZoomHelper.destroy,\n+                tools.destroy,\n+                odfCanvas.destroy,\n+                destroyInternal\n+            ]);\n+\n+            core.Async.destroyAll(destroyCallbacks, callback);\n+        }\n+\n+        // TODO:\n+        // this.openDocumentFromByteArray = openDocumentFromByteArray; see also https://github.com/kogmbh/WebODF/issues/375\n+        // setReadOnly: setReadOnly,\n+\n+        /**\n+         * Registers a callback which should be called if the given event happens.\n+         * @name TextEditor#addEventListener\n+         * @function\n+         * @param {!string} eventId\n+         * @param {!Function} callback\n+         * @return {undefined}\n+         */\n+        this.addEventListener = eventNotifier.subscribe;\n+        /**\n+         * Unregisters a callback for the given event.\n+         * @name TextEditor#removeEventListener\n+         * @function\n+         * @param {!string} eventId\n+         * @param {!Function} callback\n+         * @return {undefined}\n+         */\n+        this.removeEventListener = eventNotifier.unsubscribe;\n+\n+\n+        /**\n+         * @return {undefined}\n+         */\n+        function init() {\n+            var editorPane,\n+                /** @inner @const\n+                    @type{!string} */\n+                documentns = document.documentElement.namespaceURI;\n+\n+            /**\n+             * @param {!string} tagLocalName\n+             * @param {!string|undefined} id\n+             * @param {!string} className\n+             * @return {!Element}\n+             */\n+            function createElement(tagLocalName, id, className) {\n+                var element;\n+                element = document.createElementNS(documentns, tagLocalName);\n+                if (id) {\n+                    element.id = id;\n+                }\n+                element.classList.add(className);\n+                return element;\n+            }\n+\n+            // create needed tree structure\n+            canvasElement = createElement('div', canvasElementId, \"webodfeditor-canvas\");\n+            canvasContainerElement = createElement('div', canvasContainerElementId, \"webodfeditor-canvascontainer\");\n+            toolbarElement = createElement('span', toolbarElementId, \"webodfeditor-toolbar\");\n+            toolbarContainerElement = createElement('span', undefined, \"webodfeditor-toolbarcontainer\");\n+            editorElement = createElement('div', editorElementId, \"webodfeditor-editor\");\n+\n+            // put into tree\n+            canvasContainerElement.appendChild(canvasElement);\n+            toolbarContainerElement.appendChild(toolbarElement);\n+            editorElement.appendChild(toolbarContainerElement);\n+            editorElement.appendChild(canvasContainerElement);\n+            mainContainerElement.appendChild(editorElement);\n+\n+            // style all elements with Dojo's claro.\n+            // Not nice to do this on body, but then there is no other way known\n+            // to style also all dialogs, which are attached directly to body\n+            document.body.classList.add(\"claro\");\n+\n+            // create widgets\n+            mainContainer = new BorderContainer({}, mainContainerElementId);\n+\n+            editorPane = new ContentPane({\n+                region: 'center'\n+            }, editorElementId);\n+            mainContainer.addChild(editorPane);\n+\n+            mainContainer.startup();\n+\n+            tools = new Tools(toolbarElementId, {\n+                onToolDone: setFocusToOdfCanvas,\n+                loadOdtFile: loadOdtFile,\n+                saveOdtFile: saveOdtFile,\n+                close: close,\n+                directTextStylingEnabled: directTextStylingEnabled,\n+                directParagraphStylingEnabled: directParagraphStylingEnabled,\n+                paragraphStyleSelectingEnabled: paragraphStyleSelectingEnabled,\n+                paragraphStyleEditingEnabled: paragraphStyleEditingEnabled,\n+                imageInsertingEnabled: imageEditingEnabled,\n+                hyperlinkEditingEnabled: hyperlinkEditingEnabled,\n+                annotationsEnabled: annotationsEnabled,\n+                undoRedoEnabled: undoRedoEnabled,\n+                zoomingEnabled: zoomingEnabled,\n+                aboutEnabled: true\n+            });\n+\n+            odfCanvas = new odf.OdfCanvas(canvasElement);\n+            odfCanvas.enableAnnotations(annotationsEnabled, true);\n+\n+            odfCanvas.addListener(\"statereadychange\", createSession);\n+\n+            fullWindowZoomHelper = new FullWindowZoomHelper(toolbarContainerElement, canvasContainerElement);\n+\n+            setUserData(editorOptions.userData);\n+        }\n+\n+        init();\n+    }\n+\n+    function loadDojoAndStuff(callback) {\n+        var head = document.getElementsByTagName(\"head\")[0],\n+            link;\n+\n+        link = document.createElement(\"link\");\n+        link.rel = \"stylesheet\";\n+        link.href = installationPath + \"/app/resources/app.css\";\n+        link.type = \"text/css\";\n+        link.async = false;\n+        link.onload = function() {\n+            var link;\n+            link = document.createElement(\"link\");\n+            link.rel = \"stylesheet\";\n+            link.href = installationPath + \"/wodotexteditor.css\";\n+            link.type = \"text/css\";\n+            link.async = false;\n+            link.onload = function() {\n+                var script;\n+                script = document.createElement(\"script\");\n+                script.src = installationPath + \"/dojo-amalgamation.js\";\n+                script[\"data-dojo-config\"] = \"async: true\";\n+                script.charset = \"utf-8\";\n+                script.type = \"text/javascript\";\n+                script.async = false;\n+                script.onload = function() {\n+                    var script;\n+                    script = document.createElement(\"script\");\n+                    script.src = installationPath + \"/webodf.js\";\n+                    script.charset = \"utf-8\";\n+                    script.type = \"text/javascript\";\n+                    script.async = false;\n+                    script.onload = callback;\n+                    head.appendChild(script);\n+                };\n+                head.appendChild(script);\n+            };\n+            head.appendChild(link);\n+        };\n+        head.appendChild(link);\n+    }\n+\n+    /**\n+     * Creates a text editor object and returns it on success in the passed callback.\n+     * @name Wodo#createTextEditor\n+     * @function\n+     * @param {!string} mainContainerElementId\n+     * @param {!Object.<!string,!*>} editorOptions\n+     * @param {!function(err:?Error, editor:!TextEditor=):undefined} onEditorCreated\n+     * @return {undefined}\n+     */\n+    function createTextEditor(mainContainerElementId, editorOptions, onEditorCreated) {\n+        /**\n+         * @return {undefined}\n+         */\n+        function create() {\n+            var editor = new TextEditor(mainContainerElementId, editorOptions);\n+            onEditorCreated(null, editor);\n+        }\n+\n+        if (!isInitalized) {\n+            pendingInstanceCreationCalls.push(create);\n+            // first request?\n+            if (pendingInstanceCreationCalls.length === 1) {\n+                if (String(typeof WodoFromSource) === \"undefined\") {\n+                    loadDojoAndStuff(initTextEditor);\n+                } else {\n+                    initTextEditor();\n+                }\n+            }\n+        } else {\n+            create();\n+        }\n+    }\n+\n+\n+    return\n+    /**","path":"programs/editor/wodotexteditor.js","position":703,"original_position":703,"commit_id":"69af71c0f9cb850f44f6686cf6eb3053975e5d6f","original_commit_id":"69af71c0f9cb850f44f6686cf6eb3053975e5d6f","user":{"login":"kossebau","id":4068037,"avatar_url":"https://avatars.githubusercontent.com/u/4068037?","gravatar_id":"6ec9d5b4b917b38c571b568766c59bf4","url":"https://api.github.com/users/kossebau","html_url":"https://github.com/kossebau","followers_url":"https://api.github.com/users/kossebau/followers","following_url":"https://api.github.com/users/kossebau/following{/other_user}","gists_url":"https://api.github.com/users/kossebau/gists{/gist_id}","starred_url":"https://api.github.com/users/kossebau/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/kossebau/subscriptions","organizations_url":"https://api.github.com/users/kossebau/orgs","repos_url":"https://api.github.com/users/kossebau/repos","events_url":"https://api.github.com/users/kossebau/events{/privacy}","received_events_url":"https://api.github.com/users/kossebau/received_events","type":"User","site_admin":false},"body":"(uh, which browser? worked so far here for what ever I tested) \r\nWill see what I can do to keep JSDocs happy.","created_at":"2014-06-19T06:46:21Z","updated_at":"2014-06-19T06:46:21Z","html_url":"https://github.com/kogmbh/WebODF/pull/536#discussion_r13955292","pull_request_url":"https://api.github.com/repos/kogmbh/WebODF/pulls/536","_links":{"self":{"href":"https://api.github.com/repos/kogmbh/WebODF/pulls/comments/13955292"},"html":{"href":"https://github.com/kogmbh/WebODF/pull/536#discussion_r13955292"},"pull_request":{"href":"https://api.github.com/repos/kogmbh/WebODF/pulls/536"}}},"pull_request":{"url":"https://api.github.com/repos/kogmbh/WebODF/pulls/536","id":14624512,"html_url":"https://github.com/kogmbh/WebODF/pull/536","diff_url":"https://github.com/kogmbh/WebODF/pull/536.diff","patch_url":"https://github.com/kogmbh/WebODF/pull/536.patch","issue_url":"https://api.github.com/repos/kogmbh/WebODF/issues/536","number":536,"state":"open","title":"Simple to use editor component (non-collab for now)","user":{"login":"kossebau","id":4068037,"avatar_url":"https://avatars.githubusercontent.com/u/4068037?","gravatar_id":"6ec9d5b4b917b38c571b568766c59bf4","url":"https://api.github.com/users/kossebau","html_url":"https://github.com/kossebau","followers_url":"https://api.github.com/users/kossebau/followers","following_url":"https://api.github.com/users/kossebau/following{/other_user}","gists_url":"https://api.github.com/users/kossebau/gists{/gist_id}","starred_url":"https://api.github.com/users/kossebau/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/kossebau/subscriptions","organizations_url":"https://api.github.com/users/kossebau/orgs","repos_url":"https://api.github.com/users/kossebau/repos","events_url":"https://api.github.com/users/kossebau/events{/privacy}","received_events_url":"https://api.github.com/users/kossebau/received_events","type":"User","site_admin":false},"body":"This should be the future of the editor component(s). A simple API for 3rd-party WebODF editor interested, hiding all the internal complexity, so allowing us to throw things around as we need, without breaking things too much for that 3rd-party.\r\n\r\n`make product-wodotexteditor` will create a zip file with everything needed for the editor component. Unzipping that file and following the HOWTO should make people happy. So far I successfully managed to follow my own text in there and get happy a little :)\r\n\r\nFixes #295 \r\nPartially solved #195 (besides the compilation).","created_at":"2014-04-10T20:28:33Z","updated_at":"2014-06-19T06:46:21Z","closed_at":null,"merged_at":null,"merge_commit_sha":"95bf3b30cd9bfd8a8582368d8a29aae93fd4356b","assignee":null,"milestone":{"url":"https://api.github.com/repos/kogmbh/WebODF/milestones/2","labels_url":"https://api.github.com/repos/kogmbh/WebODF/milestones/2/labels","id":435613,"number":2,"title":"WebODF 0.5.0","description":"","creator":{"login":"kossebau","id":4068037,"avatar_url":"https://avatars.githubusercontent.com/u/4068037?","gravatar_id":"6ec9d5b4b917b38c571b568766c59bf4","url":"https://api.github.com/users/kossebau","html_url":"https://github.com/kossebau","followers_url":"https://api.github.com/users/kossebau/followers","following_url":"https://api.github.com/users/kossebau/following{/other_user}","gists_url":"https://api.github.com/users/kossebau/gists{/gist_id}","starred_url":"https://api.github.com/users/kossebau/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/kossebau/subscriptions","organizations_url":"https://api.github.com/users/kossebau/orgs","repos_url":"https://api.github.com/users/kossebau/repos","events_url":"https://api.github.com/users/kossebau/events{/privacy}","received_events_url":"https://api.github.com/users/kossebau/received_events","type":"User","site_admin":false},"open_issues":8,"closed_issues":43,"state":"open","created_at":"2013-09-24T14:23:26Z","updated_at":"2014-06-18T12:39:50Z","due_on":null},"commits_url":"https://api.github.com/repos/kogmbh/WebODF/pulls/536/commits","review_comments_url":"https://api.github.com/repos/kogmbh/WebODF/pulls/536/comments","review_comment_url":"https://api.github.com/repos/kogmbh/WebODF/pulls/comments/{number}","comments_url":"https://api.github.com/repos/kogmbh/WebODF/issues/536/comments","statuses_url":"https://api.github.com/repos/kogmbh/WebODF/statuses/69af71c0f9cb850f44f6686cf6eb3053975e5d6f","head":{"label":"kossebau:woeditor","ref":"woeditor","sha":"69af71c0f9cb850f44f6686cf6eb3053975e5d6f","user":{"login":"kossebau","id":4068037,"avatar_url":"https://avatars.githubusercontent.com/u/4068037?","gravatar_id":"6ec9d5b4b917b38c571b568766c59bf4","url":"https://api.github.com/users/kossebau","html_url":"https://github.com/kossebau","followers_url":"https://api.github.com/users/kossebau/followers","following_url":"https://api.github.com/users/kossebau/following{/other_user}","gists_url":"https://api.github.com/users/kossebau/gists{/gist_id}","starred_url":"https://api.github.com/users/kossebau/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/kossebau/subscriptions","organizations_url":"https://api.github.com/users/kossebau/orgs","repos_url":"https://api.github.com/users/kossebau/repos","events_url":"https://api.github.com/users/kossebau/events{/privacy}","received_events_url":"https://api.github.com/users/kossebau/received_events","type":"User","site_admin":false},"repo":{"id":13063894,"name":"WebODF","full_name":"kossebau/WebODF","owner":{"login":"kossebau","id":4068037,"avatar_url":"https://avatars.githubusercontent.com/u/4068037?","gravatar_id":"6ec9d5b4b917b38c571b568766c59bf4","url":"https://api.github.com/users/kossebau","html_url":"https://github.com/kossebau","followers_url":"https://api.github.com/users/kossebau/followers","following_url":"https://api.github.com/users/kossebau/following{/other_user}","gists_url":"https://api.github.com/users/kossebau/gists{/gist_id}","starred_url":"https://api.github.com/users/kossebau/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/kossebau/subscriptions","organizations_url":"https://api.github.com/users/kossebau/orgs","repos_url":"https://api.github.com/users/kossebau/repos","events_url":"https://api.github.com/users/kossebau/events{/privacy}","received_events_url":"https://api.github.com/users/kossebau/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/kossebau/WebODF","description":"WebODF - JavaScript Document Engine","fork":true,"url":"https://api.github.com/repos/kossebau/WebODF","forks_url":"https://api.github.com/repos/kossebau/WebODF/forks","keys_url":"https://api.github.com/repos/kossebau/WebODF/keys{/key_id}","collaborators_url":"https://api.github.com/repos/kossebau/WebODF/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/kossebau/WebODF/teams","hooks_url":"https://api.github.com/repos/kossebau/WebODF/hooks","issue_events_url":"https://api.github.com/repos/kossebau/WebODF/issues/events{/number}","events_url":"https://api.github.com/repos/kossebau/WebODF/events","assignees_url":"https://api.github.com/repos/kossebau/WebODF/assignees{/user}","branches_url":"https://api.github.com/repos/kossebau/WebODF/branches{/branch}","tags_url":"https://api.github.com/repos/kossebau/WebODF/tags","blobs_url":"https://api.github.com/repos/kossebau/WebODF/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/kossebau/WebODF/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/kossebau/WebODF/git/refs{/sha}","trees_url":"https://api.github.com/repos/kossebau/WebODF/git/trees{/sha}","statuses_url":"https://api.github.com/repos/kossebau/WebODF/statuses/{sha}","languages_url":"https://api.github.com/repos/kossebau/WebODF/languages","stargazers_url":"https://api.github.com/repos/kossebau/WebODF/stargazers","contributors_url":"https://api.github.com/repos/kossebau/WebODF/contributors","subscribers_url":"https://api.github.com/repos/kossebau/WebODF/subscribers","subscription_url":"https://api.github.com/repos/kossebau/WebODF/subscription","commits_url":"https://api.github.com/repos/kossebau/WebODF/commits{/sha}","git_commits_url":"https://api.github.com/repos/kossebau/WebODF/git/commits{/sha}","comments_url":"https://api.github.com/repos/kossebau/WebODF/comments{/number}","issue_comment_url":"https://api.github.com/repos/kossebau/WebODF/issues/comments/{number}","contents_url":"https://api.github.com/repos/kossebau/WebODF/contents/{+path}","compare_url":"https://api.github.com/repos/kossebau/WebODF/compare/{base}...{head}","merges_url":"https://api.github.com/repos/kossebau/WebODF/merges","archive_url":"https://api.github.com/repos/kossebau/WebODF/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/kossebau/WebODF/downloads","issues_url":"https://api.github.com/repos/kossebau/WebODF/issues{/number}","pulls_url":"https://api.github.com/repos/kossebau/WebODF/pulls{/number}","milestones_url":"https://api.github.com/repos/kossebau/WebODF/milestones{/number}","notifications_url":"https://api.github.com/repos/kossebau/WebODF/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/kossebau/WebODF/labels{/name}","releases_url":"https://api.github.com/repos/kossebau/WebODF/releases{/id}","created_at":"2013-09-24T12:37:20Z","updated_at":"2014-06-18T16:10:51Z","pushed_at":"2014-06-18T16:10:51Z","git_url":"git://github.com/kossebau/WebODF.git","ssh_url":"git@github.com:kossebau/WebODF.git","clone_url":"https://github.com/kossebau/WebODF.git","svn_url":"https://github.com/kossebau/WebODF","homepage":"http://webodf.org/","size":15046,"stargazers_count":0,"watchers_count":0,"language":"JavaScript","has_issues":false,"has_downloads":true,"has_wiki":false,"forks_count":0,"mirror_url":null,"open_issues_count":0,"forks":0,"open_issues":0,"watchers":0,"default_branch":"master"}},"base":{"label":"kogmbh:master","ref":"master","sha":"077ebf53c3cc1e6ff8616d3d3b6513ee358dcbe8","user":{"login":"kogmbh","id":5339633,"avatar_url":"https://avatars.githubusercontent.com/u/5339633?","gravatar_id":"810370be3827ed25626c4b574a90a1dd","url":"https://api.github.com/users/kogmbh","html_url":"https://github.com/kogmbh","followers_url":"https://api.github.com/users/kogmbh/followers","following_url":"https://api.github.com/users/kogmbh/following{/other_user}","gists_url":"https://api.github.com/users/kogmbh/gists{/gist_id}","starred_url":"https://api.github.com/users/kogmbh/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/kogmbh/subscriptions","organizations_url":"https://api.github.com/users/kogmbh/orgs","repos_url":"https://api.github.com/users/kogmbh/repos","events_url":"https://api.github.com/users/kogmbh/events{/privacy}","received_events_url":"https://api.github.com/users/kogmbh/received_events","type":"Organization","site_admin":false},"repo":{"id":12957983,"name":"WebODF","full_name":"kogmbh/WebODF","owner":{"login":"kogmbh","id":5339633,"avatar_url":"https://avatars.githubusercontent.com/u/5339633?","gravatar_id":"810370be3827ed25626c4b574a90a1dd","url":"https://api.github.com/users/kogmbh","html_url":"https://github.com/kogmbh","followers_url":"https://api.github.com/users/kogmbh/followers","following_url":"https://api.github.com/users/kogmbh/following{/other_user}","gists_url":"https://api.github.com/users/kogmbh/gists{/gist_id}","starred_url":"https://api.github.com/users/kogmbh/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/kogmbh/subscriptions","organizations_url":"https://api.github.com/users/kogmbh/orgs","repos_url":"https://api.github.com/users/kogmbh/repos","events_url":"https://api.github.com/users/kogmbh/events{/privacy}","received_events_url":"https://api.github.com/users/kogmbh/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/kogmbh/WebODF","description":"WebODF - JavaScript Document Engine","fork":false,"url":"https://api.github.com/repos/kogmbh/WebODF","forks_url":"https://api.github.com/repos/kogmbh/WebODF/forks","keys_url":"https://api.github.com/repos/kogmbh/WebODF/keys{/key_id}","collaborators_url":"https://api.github.com/repos/kogmbh/WebODF/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/kogmbh/WebODF/teams","hooks_url":"https://api.github.com/repos/kogmbh/WebODF/hooks","issue_events_url":"https://api.github.com/repos/kogmbh/WebODF/issues/events{/number}","events_url":"https://api.github.com/repos/kogmbh/WebODF/events","assignees_url":"https://api.github.com/repos/kogmbh/WebODF/assignees{/user}","branches_url":"https://api.github.com/repos/kogmbh/WebODF/branches{/branch}","tags_url":"https://api.github.com/repos/kogmbh/WebODF/tags","blobs_url":"https://api.github.com/repos/kogmbh/WebODF/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/kogmbh/WebODF/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/kogmbh/WebODF/git/refs{/sha}","trees_url":"https://api.github.com/repos/kogmbh/WebODF/git/trees{/sha}","statuses_url":"https://api.github.com/repos/kogmbh/WebODF/statuses/{sha}","languages_url":"https://api.github.com/repos/kogmbh/WebODF/languages","stargazers_url":"https://api.github.com/repos/kogmbh/WebODF/stargazers","contributors_url":"https://api.github.com/repos/kogmbh/WebODF/contributors","subscribers_url":"https://api.github.com/repos/kogmbh/WebODF/subscribers","subscription_url":"https://api.github.com/repos/kogmbh/WebODF/subscription","commits_url":"https://api.github.com/repos/kogmbh/WebODF/commits{/sha}","git_commits_url":"https://api.github.com/repos/kogmbh/WebODF/git/commits{/sha}","comments_url":"https://api.github.com/repos/kogmbh/WebODF/comments{/number}","issue_comment_url":"https://api.github.com/repos/kogmbh/WebODF/issues/comments/{number}","contents_url":"https://api.github.com/repos/kogmbh/WebODF/contents/{+path}","compare_url":"https://api.github.com/repos/kogmbh/WebODF/compare/{base}...{head}","merges_url":"https://api.github.com/repos/kogmbh/WebODF/merges","archive_url":"https://api.github.com/repos/kogmbh/WebODF/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/kogmbh/WebODF/downloads","issues_url":"https://api.github.com/repos/kogmbh/WebODF/issues{/number}","pulls_url":"https://api.github.com/repos/kogmbh/WebODF/pulls{/number}","milestones_url":"https://api.github.com/repos/kogmbh/WebODF/milestones{/number}","notifications_url":"https://api.github.com/repos/kogmbh/WebODF/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/kogmbh/WebODF/labels{/name}","releases_url":"https://api.github.com/repos/kogmbh/WebODF/releases{/id}","created_at":"2013-09-19T19:57:49Z","updated_at":"2014-06-18T12:39:16Z","pushed_at":"2014-06-18T12:39:19Z","git_url":"git://github.com/kogmbh/WebODF.git","ssh_url":"git@github.com:kogmbh/WebODF.git","clone_url":"https://github.com/kogmbh/WebODF.git","svn_url":"https://github.com/kogmbh/WebODF","homepage":"http://webodf.org/","size":22336,"stargazers_count":178,"watchers_count":178,"language":"JavaScript","has_issues":true,"has_downloads":true,"has_wiki":true,"forks_count":36,"mirror_url":null,"open_issues_count":177,"forks":36,"open_issues":177,"watchers":178,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/kogmbh/WebODF/pulls/536"},"html":{"href":"https://github.com/kogmbh/WebODF/pull/536"},"issue":{"href":"https://api.github.com/repos/kogmbh/WebODF/issues/536"},"comments":{"href":"https://api.github.com/repos/kogmbh/WebODF/issues/536/comments"},"review_comments":{"href":"https://api.github.com/repos/kogmbh/WebODF/pulls/536/comments"},"review_comment":{"href":"https://api.github.com/repos/kogmbh/WebODF/pulls/comments/{number}"},"commits":{"href":"https://api.github.com/repos/kogmbh/WebODF/pulls/536/commits"},"statuses":{"href":"https://api.github.com/repos/kogmbh/WebODF/statuses/69af71c0f9cb850f44f6686cf6eb3053975e5d6f"}}}}