From 3e840d991046704ad1dc010a52dc2a39f7eeea24 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=B6schele?= Date: Thu, 22 Feb 2018 13:41:07 +0100 Subject: [PATCH 1/9] Added UI translation for jquery.xmleditor Added readonly mode for the editor Added switches to hide UI elements when not needed Added parameter to choose the initial display (XML or Text) --- README.md | 6 + bower.json | 10 +- jquery.xmleditor.js | 780 +++++++++++++++++++++++++-------- src/abstract_xml_object.js | 20 +- src/add_node_menu.js | 14 +- src/attribute_menu.js | 2 +- src/document_state.js | 11 +- src/format_xml.js | 3 +- src/gui_editor.js | 9 +- src/jquery.xmleditor.js | 436 +++++++++++++++--- src/menu_bar.js | 200 +++++---- src/modify_element_menu.js | 1 + src/modify_menu_panel.js | 8 +- src/namespace_list.js | 2 +- src/schema_tree.js | 3 +- src/text_editor.js | 8 +- src/undo_history.js | 1 + src/xml_attribute.js | 10 +- src/xml_autocomplete.js | 2 +- src/xml_cdata_node.js | 2 +- src/xml_comment_node.js | 2 +- src/xml_element.js | 62 +-- src/xml_element_stub.js | 2 +- src/xml_templates.js | 7 +- src/xml_text_node.js | 3 +- src/xml_unspecified_element.js | 4 +- xsd/src/schema_manager.js | 134 ++++-- xsd/src/schema_processor.js | 128 +++--- xsd/xsd2json.js | 281 +++++++----- 29 files changed, 1564 insertions(+), 587 deletions(-) diff --git a/README.md b/README.md index 59819a5..f6f27a0 100644 --- a/README.md +++ b/README.md @@ -173,6 +173,12 @@ Providing a submitButtonConfigs option will override the creation of the standar - menuEntries - A list of additional entries to add to the top menu bar, such as adding new help entries. - confirmExitWhenUnsubmitted - Causes web browsers to prompt users if they try to navigate away from the editor while there are unsubmitted changes. Valid values: True or false. - undoHistorySize - The number of history states remembered by the undo/redo feature. Default is 20. +- showExport - This is true by default - allows to hide the "Export" button. +- enableEdit - This is true by default - if set to false, the editor will behave as a viewer and will also set the ace editor to readonly (plus removing some menus). +- sourceDesignSwitch - This is true by default - allows to hide the switch between Text and XML display +- initialEditMode - Switch to predefined view: XML or HTML-DIV editor. 0=HTML-DIV editor, 1=XML Source editor +- i18n - This is a map in form ["en"] or ["de"] which holds all the labels used in the application for translation. More translations can be added. + ### Building the plugin yourself If we wish to build the combined jquery.xmleditor.js yourself, you can use the provided rake script. With rake installed, simple type "rake" in the root directory of this project. diff --git a/bower.json b/bower.json index 001b56e..41799ea 100644 --- a/bower.json +++ b/bower.json @@ -1,12 +1,14 @@ { "name": "jquery.xmleditor", "main": "jquery.xmleditor.js", - "version": "1.1.0", + "version": "1.3.1", "homepage": "https://github.com/UNC-Libraries/jquery.xmleditor", "authors": [ - "Ben Pennell ", - "Mike Daines ", - "Dean Farrell " + "Ben Pennell (https://github.com/bbpennel)", + "Mike Daines (https://github.com/mdaines)", + "Dean Farrell ", + "Volker Diels-Grabsch (https://github.com/m-click)", + "Marc Höschele (https://github.com/mhoesche)" ], "description": "A web browser based XML editor. It provides a general use graphical tool for creating new or modifying existing XML documents in your web browser. Information is extracted from an XML schema (XSD file) to provide the user with information about what elements, subelements and attributes are available at different points in the structure, and a GUI based means of adding or removing them from the document.", "keywords": [ diff --git a/jquery.xmleditor.js b/jquery.xmleditor.js index b367097..84e2306 100644 --- a/jquery.xmleditor.js +++ b/jquery.xmleditor.js @@ -1,5 +1,6 @@ ;(function($){ - + //= require_self +//= require_tree . /* @@ -74,6 +75,30 @@ var localName = function(node) { return node.nodeName.substring(index + 1); }; +// Extracts and returns URL parameters or the given default. +function getParam(name, def) { + name = name.replace(/[\[]/, "\\[").replace(/[\]]/, "\\]"); + var regex = new RegExp("[\\?&]" + name + "=([^&#]*)"), + results = regex.exec(location.search); + return results === null ? def : decodeURIComponent(results[1].replace(/\+/g, " ")); +} + +// Add method to String. +String.prototype.replaceAll = function(search, replace) { + //if replace is null, return original string otherwise it will + //replace search string with 'undefined'. + if(!replace) + return this; + return this.replace(new RegExp('[' + search + ']', 'g'), replace); +}; + +// Extract the user language first from the browsers default and let it overwrite via URL parameter "lang=". +var userLang = navigator.language || navigator.userLanguage; +userLang = getParam("lang", userLang); +if ("de" != userLang && "en" != userLang) { + userLang = "en"; +} + $.widget( "xml.xmlEditor", { options: { // Schema object to be used @@ -106,11 +131,6 @@ $.widget( "xml.xmlEditor", { elementUpdated : undefined, // Title for the document, displayed in the header documentTitle : null, - addTopMenuHeaderText : 'Add Top Element', - addAttrMenuHeaderText : 'Add Attribute', - addElementMenuHeaderText : 'Add Subelement', - xmlEditorLabel : 'XML', - textEditorLabel : 'Text', // Set to false to get rid of the enableDocumentStatusPanel : true, @@ -131,7 +151,276 @@ $.widget( "xml.xmlEditor", { prependNewElements: false, autocomplete: true, - targetNS: null + targetNS: null, + + // Flag to disable the export button + showExport: true, + + // Switch to readonly mode + enableEdit: true, + + // Hide the ability to switch between the modes + sourceDesignSwitch: true, + + // Switch to predefined view: XML or HTML-DIV editor. 0=HTML-DIV editor, 1=XML Source editor + initialEditMode: 0, + + // i18n resources + i18n: { + "en": { + uploadedFiles:"Uploaded files:", + addNodes:"Add nodes", + addRoot:"Add top element", + addAttribute:"Add attribute", + addElement:"Add Element", + addSubelement:"Add subelement", + insert:"Insert", + xml:"Design", + text:"Source", + editing:"Editing ", + noBrowserExportSupport:"Browser does not support saving files via this editor. To save, copy and paste the document from the Text view.", + filename:"Filename", + saveFailedXmlInvalid:"The XML is not valid. The content cannot be processed.", + saveFailedSeeErrors:"Save failed
See error message.", + export:"Export", + download:"Download", + submitting:"Processing...", + failedToSubmit:"The XML document could not be successfully submitted", + documentUnchanged:"No changes", + submitChanges:"Apply", + noElements:"There are no elements in this document. Use the menu on the right to add new top level elements.", + unsavedChanges:"Unapplied changes", + savedChanges:"Changes applied.", + couldNotAdd1:"Could not add child ", + couldNotAdd2:", it is not a valid child of ", + couldNotAddAttr1:"Could not add attribute ", + couldNotAddAttr2:", it is not a valid for element ", + noDocument:"Could not load specified document and no fallback provided, cannot start.", + noStartingDocument:"No starting document.", + failedToAddChild:"Failed to add child of type ", + undo:"ctrl+z", + redo:"ctrl+y", + del:"del", + enter:"enter", + elementUp:"alt+up", + elementDown:"alt+down", + esc:"esc", + down:"down", + up:"up", + left:"left", + right:"right", + shiftDown:"shift+down", + shiftUp:"shift+up", + shiftLeft:"shift+left", + shiftRight:"shift+right", + altA:"alt+a", + ltShiftE:"alt+shift+e", + altShiftX:"alt+shift+x", + altShiftS:"alt+shift+s", + altShiftT:"alt+shift+t", + file:"File", + edit:"Edit", + select:"Select", + view:"View", + options:"Options", + deselect:"Deselect", + nextElement:"Next Element", + previousElement:"Previous Element", + parentElement:"Parent", + firstChild:"First Child", + nextSibling:"Next Sibling", + previousSibling:"Previous Sibling", + nextAttribute:"Next Attribute", + previousAttribute:"Previous Attribute", + deleteElement:"Delete", + moveElementUp:"Move up", + moveElementDown:"Move down", + undoMenuitem:"Undo", + redoMenuitem:"Redo", + switchToXml:"Graphical XML", + switchToText:"Raw XML", + prettifyXml:"Automatic XML formatting", + enableShortcuts:"Enable shortcuts", + enforceMinMaxOccurs:"Enforce Min/Max", + prependNewElements:"Prepend adding new elements", + noConnection:"No connection.\nPlease check network.", + pageNotFound:"The requested page could not be found. [404]", + internalServerError:"Internal server error [500].", + jsonParseFailed:"JSON parse failed.", + timeout:"timeout while contacting server.", + ajaxAborted:"AJAX request aborted.", + uncaughtError:"Unexpected error\n", + xmlSerializerNotSupported:"XML Serializer not supported.", + alreadyExistent:"Already existent", + useTheMenuToAddSubElementsAndAttr:"Use the menu to add subelements and attributes.", + useTheMenuToAddAttr:"Use the menu to add attributes.", + useTheMenuToAddSubElements:"Use the menu to add subelements.", + cannotSubmitNoPostOption:"Cannot submit because no post Options", + useMenuToAdd:"Use the menu to add subelements, attributes and text.", + useMenuToAddAttributesText:"Use the menu to add attributes and text.", + useMenuToAddSubelementsText:"Use the menu to add subelements and text.", + useMenuToAddText:"Use the menu to add text.", + useMenuToAddSubelementsAttributes:"Use the menu to add subelements and attributes.", + useMenuToAddAttributes:"Use the menu to add attributes.", + useMenuToAddSubelements:"Use the menu to add subelements.", + unableToAddFixSyntax:"Unable to add element, please fix existing XML syntax first.", + addCData:"Add CDATA", + addComment:"Add Comment", + addText:"Add Text", + add:"Add ", + xmlDocErrorInLine:"Error in line ", + xmlDocErrorAtPos:" position ", + xmlDocErrorCode:"Error Code: ", + xmlDocErrorReason:"Error Reason: ", + xmlDocErrorLine:"Error Line: ", + addChildElement:"Add child element", + addSiblingElement:"Add sibling element", + addElementToParent:"Add element to parent", + addElementToRoot:"Add element to root", + addTextToElement:"Add text to element", + addCommentToElement:"Add comment to element", + addCDataToElement:"Add CDATA to element", + altE:"alt+2", + altS:"alt+s", + altP:"alt+p", + altR:"alt+r", + altT:"alt+t", + altSlash:"alt+/", + altComma:"alt+,", + cancel:"Cancel", + choose:"Choose", + unableToLoadTemplate:"Unable to load the requested template: ", + useMenuToAddContents:"Use the menu to add contents." + }, + "de": { + uploadedFiles:"Hochgeladene Dateien:", + addNodes:"Elemente hinzufügen", + addRoot:"Hauptelement hinzufügen", + addAttribute:"Attribut hinzufügen", + addElement:"Element hinzufügen", + addSubelement:"Unterelement hinzufügen", + insert:"Einfügen", + xml:"Design", + text:"Quelle", + editing:"Bearbeite ", + noBrowserExportSupport:"Ihr Webbrowser unterstützt das übernehmen der Inhalte nicht. Um die Inhalte zu speichern wechseln sie bitte in the Textansicht und markieren und kopieren sie den Inhalt.", + filename:"Dateiname", + saveFailedXmlInvalid:"XML ist ungültig. Daten können nicht verarbeitet werden.", + saveFailedSeeErrors:"Die Übernahme der Änderungen ist fehlgeschlagen
Siehe Fehlermeldung.", + export:"Export", + download:"Download", + submitting:"Verarbeite...", + failedToSubmit:"Das XML Dokument konnte nicht verabeitet werden", + documentUnchanged:"Keine Änderungen", + submitChanges:"Übernehmen", + noElements:"Das XML Dokument ist leer. Verwenden sie das Menü rechts um ein neuen Hauptelement einzufügen.", + unsavedChanges:"Nicht übernommene Änderungen", + savedChanges:"Änderungen gespeichert", + couldNotAdd1:"Das Kind-Element ", + couldNotAdd2:" kann nicht hinzugefügt werden, da es kein gültiges Kind des folgenden Tags is: ", + couldNotAddAttr1:"Das Attribut ", + couldNotAddAttr2:"kann nicht hinzugefügt werden, da es kein gültiges Attribut des folgenden Tags ist: ", + noDocument:"Das angegebene Dokument konnte nicht geladen werden und es ist kein Rückfalldokument definiert. Der Editor kann nicht starten.", + noStartingDocument:"Es ist kein Startdokument definiert.", + failedToAddChild:"Das Element des folgenden Typs konnte nicht hinzugefügt werden: ", + undo:"Strg+z", + redo:"Strg+y", + del:"Entf", + enter:"Enter", + elementUp:"Alt+oben", + elementDown:"Alt+unten", + esc:"Esc", + down:"unten", + up:"oben", + left:"links", + right:"rechts", + shiftDown:"Umschalt+unten", + shiftUp:"Umschalt+oben", + shiftLeft:"Umschalt+links", + shiftRight:"Umschalt+rechts", + altA:"alt+a", + altShiftE:"Alt+Umschalt+e", + altShiftX:"Alt+Umschalt+x", + altShiftS:"Alt+Umschalt+s", + altShiftT:"Alt+Umschalt+t", + file:"Datei", + edit:"Bearbeiten", + select:"Auswählen", + view:"Ansicht", + options:"Optionen", + deselect:"Abwählen", + nextElement:"Nächstes Element", + previousElement:"Vorheriges Element", + parentElement:"Eine Ebene höher", + firstChild:"Erstes Unterelement", + nextSibling:"Nächstes Paar", + previousSibling:"Vorheriges Paar", + nextAttribute:"Nächstes Attribut", + previousAttribute:"Vorheriges Attribut", + deleteElement:"Löschen", + moveElementUp:"nach oben", + moveElementDown:"nach unten", + undoMenuitem:"Rückgängig", + redoMenuitem:"Wiederherstellen", + switchToXml:"Grafische XML Anzeige", + switchToText:"XML-Quelle", + prettifyXml:"Automatische XML Formatierung", + enableShortcuts:"Direktzugriffstasten aktiv", + enforceMinMaxOccurs:"Min/Max Vorgaben erzwingen", + prependNewElements:"Keine neuen Elemente zulassen", + noConnection:"Kann keine Verbindung aufbauen.\nBitte Netzwerkverbindung prüfen.", + pageNotFound:"Die angeforderte Seite wurde nicht gefunden. [404]", + internalServerError:"Interner Server Fehler [500].", + jsonParseFailed:"Parsen des angeforderten JSON ist fehlgeschlagen.", + timeout:"Zeitüberschreitung der Serververbindung.", + ajaxAborted:"AJAX Anfrage abgebrochen.", + uncaughtError:"Unerwarteter Fehler.\n", + xmlSerializerNotSupported:"XML Serialisierung nicht unterstützt.", + alreadyExistent:"Existiert bereits", + useTheMenuToAddSubElementsAndAttr:"Weitere Elemente und Attribute über das Menü rechts hinzufügen.", + useTheMenuToAddAttr:"Weitere Attribute über das Menü hinzufügen.", + useTheMenuToAddSubElements:"Weitere Elemente über das Menü rechts hinzufügen.", + cannotSubmitNoPostOption:"Datei kann nicht übertragen werden - keine HTTP POST Optionen definiert.", + useMenuToAdd:"Unterelemente, Attribute und Text können über das Menü hinzugefügt werden.", + useMenuToAddAttributesText:"Attribute und Text können über das Menü hinzugefügt werden.", + useMenuToAddSubelementsText:"Unterelemente und Text können über das Menü hinzugefügt werden.", + useMenuToAddText:"Text kann über das Menü hinzugefügt werden.", + useMenuToAddSubelementsAttributes:"Unterelemente und Attribute können über das Menü hinzugefügt werden.", + useMenuToAddAttributes:"Attribute können über das Menü hinzugefügt werden.", + useMenuToAddSubelements:"Unterelemente können über das Menü hinzugefügt werden.", + unableToAddFixSyntax:"Element kann nicht hinzugefügt werden. Bitte zuerst die XML Syntax korrigieren.", + addCData:"CDATA hinzufügen", + addComment:"Kommentar hinzufügen", + addText:"Text hinzufügen", + add:"Hinzufügen von ", + xmlDocErrorInLine:"Fehler in der Zeile ", + xmlDocErrorAtPos:" an Position ", + xmlDocErrorCode:"Fehlercode: ", + xmlDocErrorReason:"Fehlergrund: ", + xmlDocErrorLine:"Fehlerzeile: ", + addChildElement:"Kind-Element hinzufügen", + addSiblingElement:"Schwesterelement hinzufügen", + addElementToParent:"Element zum übergeordneten Element hinzufügen", + addElementToRoot:"Element zum Wurzelelement hinzufügen", + addTextToElement:"Text zum Element hinzufügen", + addCommentToElement:"Kommentar zum Element hinzufügen", + addCDataToElement:"CDATA zum Element hinzufügen", + altE:"Alt+2", + altS:"Alt+s", + altP:"Alt+p", + altR:"Alt+r", + altT:"Alt+t", + altSlash:"Alt+/", + altComma:"Alt+,", + cancel:"Abbrechen", + choose:"Auswählen", + unableToLoadTemplate:"Die gewählte Vorlage konnte nicht geladen werden: ", + useMenuToAddContents:"Inhalte können über das Menü hinzugefügt werden." + } + }, + + userLang: getParam("lang", "en") + }, _create: function() { @@ -240,19 +529,19 @@ $.widget( "xml.xmlEditor", { if (this.options.submitErrorHandler == null) { this.options.submitErrorHandler = function(jqXHR, exception) { if (jqXHR.status === 0) { - alert('Not connect.\n Verify Network.'); + alert(this.options.i18n[this.options.userLang].noConnection); } else if (jqXHR.status == 404) { - alert('Requested page not found. [404]'); + alert(this.options.i18n[this.options.userLang].pageNotFound); } else if (jqXHR.status == 500) { - alert('Internal Server Error [500].'); + alert(this.options.i18n[this.options.userLang].internalServerError); } else if (exception === 'parsererror') { - alert('Requested JSON parse failed.'); + alert(this.options.i18n[this.options.userLang].jsonParseFailed); } else if (exception === 'timeout') { - alert('Time out error.'); + alert(this.options.i18n[this.options.userLang].timeout); } else if (exception === 'abort') { - alert('Ajax request aborted.'); + alert(this.options.i18n[this.options.userLang].ajaxAborted); } else { - alert('Uncaught Error.\n' + jqXHR.responseText); + alert(this.options.i18n[this.options.userLang].uncaughtError + jqXHR.responseText); } }; } @@ -312,7 +601,7 @@ $.widget( "xml.xmlEditor", { if (this.options.confirmExitWhenUnsubmitted) { $(window).bind('beforeunload', function(e) { if (self.xmlState != null && self.xmlState.isChanged()) { - return "The document contains unsaved changes."; + return this.options.i18n[this.options.userLang].unsavedChanges; } }); } @@ -322,6 +611,8 @@ $.widget( "xml.xmlEditor", { // Start loading the document for editing this.loadDocument(this.options.ajaxOptions, localXMLContent); + var editor = this.activeEditor; + setTimeout(function () { if (!!editor['selectNext']) { editor.selectNext(); } }, 200); }, // Load the schema object @@ -404,7 +695,7 @@ $.widget( "xml.xmlEditor", { // Document path didn't retrieve anything self._templating(); } else { - console.error("Could not specified document and no fallback provided, cannot start."); + console.error(self.options.i18n[self.options.userLang].noDocument); } } }); @@ -415,7 +706,7 @@ $.widget( "xml.xmlEditor", { // Fall back to templating if it was specified this._templating(); } else { - console.error("No starting document"); + console.error(self.options.i18n[self.options.userLang].noStartingDocument); } }, @@ -493,6 +784,8 @@ $.widget( "xml.xmlEditor", { this.constructEditor(); this.refreshDisplay(); this.activeEditor.selectRoot(); + this.modeChange(this.options.initialEditMode); // Optional initial source view change + this.refreshDisplay(); // Capture baseline undo state this.undoHistory.captureSnapshot(); }, @@ -506,7 +799,7 @@ $.widget( "xml.xmlEditor", { var editorHeaderBacking = $("
").addClass(editorHeaderClass + "_backing").appendTo(this.xmlWorkAreaContainer); this.editorHeader = $("
").attr('class', editorHeaderClass).appendTo(this.xmlWorkAreaContainer); if (this.options.documentTitle != null) - $("

").html("Editing Description: " + this.options.documentTitle).appendTo(this.editorHeader); + $("

").html(this.options.i18n[this.options.userLang].editing + this.options.documentTitle).appendTo(this.editorHeader); this.menuBar.render(this.editorHeader); editorHeaderBacking.height(this.editorHeader.outerHeight()); // Create grouping of header elements that need to be positioned together @@ -522,12 +815,12 @@ $.widget( "xml.xmlEditor", { $(window).resize($.proxy(this.resize, this)); this.modifyMenu.initialize(this.xmlEditorContainer); - this.modifyMenu.addMenu(addElementMenuClass, this.options.addElementMenuHeaderText, + this.modifyMenu.addMenu(addElementMenuClass, this.options.i18n[this.options.userLang].addSubelement, true, false, true); - this.modifyMenu.addAttributeMenu(addAttrMenuClass, this.options.addAttrMenuHeaderText, + this.modifyMenu.addAttributeMenu(addAttrMenuClass, this.options.i18n[this.options.userLang].addAttribute, true, false, true); - this.modifyMenu.addNodeMenu(addNodeMenuClass, "Add Nodes", true, false); - this.addTopLevelMenu = this.modifyMenu.addMenu(addTopMenuClass, this.options.addTopMenuHeaderText, + this.modifyMenu.addNodeMenu(addNodeMenuClass, this.options.i18n[this.options.userLang].addNodes, true, false); + this.addTopLevelMenu = this.modifyMenu.addMenu(addTopMenuClass, this.options.i18n[this.options.userLang].addRoot, true, true, false, function(target) { var selectedElement = self.guiEditor.selectedElement; if (!selectedElement || selectedElement.length == 0 || selectedElement.isRootElement) @@ -553,7 +846,7 @@ $.widget( "xml.xmlEditor", { // Resize event for refreshing menu and editor sizes resize: function () { - this.xmlTabContainer.width(this.xmlEditorContainer.outerWidth() - this.modifyMenu.menuColumn.outerWidth()); + this.xmlTabContainer.width(this.xmlEditorContainer.outerWidth() - (this.modifyMenu.menuColumn != null ? this.modifyMenu.menuColumn.outerWidth() : 0)); if (this.activeEditor != null){ this.activeEditor.resize(); } @@ -561,6 +854,9 @@ $.widget( "xml.xmlEditor", { if (this.options.floatingMenu) { this.modifyMenu.setMenuPosition(); } + if (!this.options.enableEdit && this.modifyMenu.menuColumn != null) { // no edit enabled => don't show modify menu + this.modifyMenu.menuColumn.style.width = "0px"; + } }, // Event which triggers the creation of a new child element, as defined by an instigator such as a menu @@ -583,7 +879,7 @@ $.widget( "xml.xmlEditor", { try { this.setXMLFromEditor(); } catch (e) { - this.addProblem("Unable to add element, please fix existing XML syntax first.", e); + this.addProblem(this.options.i18n[this.options.userLang].unableToAddFixSyntax, e); return; } } @@ -628,7 +924,7 @@ $.widget( "xml.xmlEditor", { } if (newElement == null) { - return "Failed to add child of type " + newElementDefinition; + return this.options.i18n[this.options.userLang].failedToAddChild + newElementDefinition; } // Trigger post element creation event in the currently active editor to handle UI updates @@ -676,7 +972,7 @@ $.widget( "xml.xmlEditor", { } if (!objectType && !xmlElement.objectType.anyAttribute) { - return "Could not add attribute " + attrDefinition + ", it is not a valid for element " + xmlElement.objectType.localName; + return this.options.i18n[this.options.userLang].couldNotAddAttr1 + attrDefinition + this.options.i18n[this.options.userLang].couldNotAddAttr2 + xmlElement.objectType.localName; } } @@ -778,10 +1074,12 @@ $.widget( "xml.xmlEditor", { } if (mode == 0) { this.activeEditor = this.guiEditor; - $("#" + xmlMenuHeaderPrefix + this.options.xmlEditorLabel.replace(/ /g, "_")).addClass("active_mode_tab"); + var editor = this.activeEditor; + setTimeout(function() {editor.selectNext();}, 200); + $("#" + xmlMenuHeaderPrefix + this.options.i18n[this.options.userLang].xml.replace(/ /g, "_")).addClass("active_mode_tab"); } else { this.activeEditor = this.textEditor; - $("#" + xmlMenuHeaderPrefix + this.options.textEditorLabel.replace(/ /g, "_")).addClass("active_mode_tab"); + $("#" + xmlMenuHeaderPrefix + this.options.i18n[this.options.userLang].text.replace(/ /g, "_")).addClass("active_mode_tab"); } this.activeEditor.activate(); if (this.ready) @@ -797,7 +1095,13 @@ $.widget( "xml.xmlEditor", { if (this.options.floatingMenu) { this.modifyMenu.setMenuPosition(); } - this.xmlWorkAreaContainer.width(this.xmlEditorContainer.outerWidth() - this.modifyMenu.menuColumn.outerWidth()); + try { + if (this.modifyMenu.menuColumn != null) { + this.xmlWorkAreaContainer.width(this.xmlEditorContainer.outerWidth() - this.modifyMenu.menuColumn.outerWidth()); + } + } catch(e) { + console.log(e); + } }, setTextArea : function(xmlString) { @@ -815,6 +1119,25 @@ $.widget( "xml.xmlEditor", { } }, + getXMLString: function() { + if (this.textEditor.active) { + var xmlString = this.textEditor.aceEditor.getValue(); + try { + this.xmlState.setXMLFromString(xmlString); + } catch (e) { + // Ignore error, continue to return last GUI value instead + } + } + return this.xml2Str(this.xmlState.xml); + }, + + getText: function() { + if (this.textEditor.active) { + return this.textEditor.aceEditor.getValue(); + } + return this.xml2Str(this.xmlState.xml); + }, + // Callback for submit button pressing. Performs a submit function and then uploads the // document to the provided URL, if configured to do either submitXML: function(config) { @@ -830,12 +1153,12 @@ $.widget( "xml.xmlEditor", { // Export the contents of the editor as text to a file, as supported by browsers exportXML: function() { if (typeof(Blob) === "undefined") { - this.addProblem("Browser does not support saving files via this editor. To save, copy and paste the document from the Text view."); + this.addProblem(this.options.i18n[this.options.userLang].noBrowserExportSupport); return false; } - var exportDialog = $("
") - .dialog({modal: true, dialogClass: 'xml_dialog', resizable : false, title: 'Enter file name', height: 80}); + var exportDialog = $("
") + .dialog({modal: true, dialogClass: 'xml_dialog', resizable : false, title: this.options.i18n[this.options.userLang].filename, height: 80}); var self = this; exportDialog.submit(function(){ if (self.textEditor.active) { @@ -843,8 +1166,8 @@ $.widget( "xml.xmlEditor", { self.setXMLFromEditor(); } catch (e) { self.xmlState.setDocumentHasChanged(true); - $("." + submissionStatusClass).html("Failed to save
See errors at top").css("background-color", "#ffbbbb").animate({backgroundColor: "#ffffff"}, 1000); - self.addProblem("Cannot save due to invalid xml", e); + $("." + submissionStatusClass).html(this.options.i18n[this.options.userLang].saveFailedSeeErrors).css("background-color", "#ffbbbb").animate({backgroundColor: "#ffffff"}, 1000); + self.addProblem(this.options.i18n[this.options.userLang].saveFailedXmlInvalid, e); return false; } } @@ -856,7 +1179,7 @@ $.widget( "xml.xmlEditor", { var fileName = exportDialog.find('input[type="text"]').val(); if (!fileName) fileName = "file.xml"; - var download = $('Download ' + fileName + '').attr("href", url); + var download = $('' + this.options.i18n[this.options.userLang].download + ' ' + fileName + '').attr("href", url); download.attr("download", fileName); exportDialog.empty().append(download); return false; @@ -869,24 +1192,25 @@ $.widget( "xml.xmlEditor", { if (this.submitButtonConfigs.length > 0 && this.submitButtonConfigs[0].url) { config = this.submitButtonConfigs[0]; } else { - this.addProblem("Cannot submit because no post Options"); + this.addProblem(this.options.i18n[this.options.userLang].cannotSubmitNoPostOption); return; } } + $(':focus').blur(); // Ensure focus is removed before saving, so that finished content saved if (this.textEditor.active) { try { this.setXMLFromEditor(); } catch (e) { this.xmlState.setDocumentHasChanged(true); - $("." + submissionStatusClass).html("Failed to submit
See errors at top").css("background-color", "#ffbbbb").animate({backgroundColor: "#ffffff"}, 1000); - this.addProblem("Cannot submit due to invalid xml", e); + $("." + submissionStatusClass).html(this.options.i18n[this.options.userLang].saveFailedSeeErrors).css("background-color", "#ffbbbb").animate({backgroundColor: "#ffffff"}, 1000); + this.addProblem(this.options.i18n[this.options.userLang].saveFailedXmlInvalid, e); return false; } } // convert XML DOM to string var xmlString = this.xml2Str(this.xmlState.xml); - $("." + submissionStatusClass).html("Submitting..."); + $("." + submissionStatusClass).html(this.options.i18n[this.options.userLang].submitting); var self = this; $.ajax({ url : config.url, @@ -903,8 +1227,8 @@ $.widget( "xml.xmlEditor", { self.clearProblemPanel(); } else { self.xmlState.syncedChangeEvent(); - $("." + submissionStatusClass).html("Failed to submit
See errors at top").css("background-color", "#ffbbbb").animate({backgroundColor: "#ffffff"}, 1000); - self.addProblem("Failed to submit xml document", outcome); + $("." + submissionStatusClass).html(this.options.i18n[this.options.userLang].saveFailedSeeErrors).css("background-color", "#ffbbbb").animate({backgroundColor: "#ffffff"}, 1000); + self.addProblem(this.options.i18n[this.options.userLang].failedToSubmit, outcome); } }, error : function(jqXHR, exception) { @@ -944,7 +1268,7 @@ $.widget( "xml.xmlEditor", { // Internet Explorer. return xmlNode.xml; } catch (e) { - this.addProblem('Xmlserializer not supported', e); + this.addProblem(this.options.i18n[this.options.userLang].xmlSerializerNotSupported, e); return false; } } @@ -995,11 +1319,15 @@ $.widget( "xml.xmlEditor", { setEnableKeybindings : function(enable) { if (enable) { this.options.enableGUIKeybindings = true; - this.menuBar.menuBarContainer.removeClass("xml_bindings_disabled"); + if (this.menuBar.menuBarContainer) { + this.menuBar.menuBarContainer.removeClass("xml_bindings_disabled"); + } $(window).on("keydown.xml_keybindings", $.proxy(this.keydownCallback, this)); } else { this.options.enableGUIKeybindings = false; - this.menuBar.menuBarContainer.addClass("xml_bindings_disabled"); + if (this.menuBar.menuBarContainer) { + this.menuBar.menuBarContainer.addClass("xml_bindings_disabled"); + } $(window).off("keydown.xml_keybindings"); } }, @@ -1177,22 +1505,31 @@ $.widget( "xml.xmlEditor", { // Menu Update functions refreshMenuUndo: function(self) { if (self.undoHistory.headIndex > 0) { - $("#" + xmlMenuHeaderPrefix + "Undo").removeClass("disabled").data("menuItemData").enabled = true; + $("#" + xmlMenuHeaderPrefix + self.options.i18n[self.options.userLang].undoMenuitem.replaceAll(' ','_')).removeClass("disabled").data("menuItemData").enabled = true; } else { - $("#" + xmlMenuHeaderPrefix + "Undo").addClass("disabled").data("menuItemData").enabled = false; + $("#" + xmlMenuHeaderPrefix + self.options.i18n[self.options.userLang].undoMenuitem.replaceAll(' ','_')).addClass("disabled").data("menuItemData").enabled = false; } if (self.undoHistory.headIndex < self.undoHistory.states.length - 1) { - $("#" + xmlMenuHeaderPrefix + "Redo").removeClass("disabled").data("menuItemData").enabled = true; + $("#" + xmlMenuHeaderPrefix + self.options.i18n[self.options.userLang].redoMenuitem.replaceAll(' ','_')).removeClass("disabled").data("menuItemData").enabled = true; } else { - $("#" + xmlMenuHeaderPrefix + "Redo").addClass("disabled").data("menuItemData").enabled = false; + $("#" + xmlMenuHeaderPrefix + self.options.i18n[self.options.userLang].redoMenuitem.replaceAll(' ','_')).addClass("disabled").data("menuItemData").enabled = false; } }, // Performs updates to the menu for changing element/attribute selection refreshMenuSelected: function(self) { - var suffixes = ['Deselect', 'Next_Element', 'Previous_Element', 'Parent', 'First_Child', 'Next_Sibling', - 'Previous_Sibling', 'Next_Attribute', 'Previous_Attribute', 'Delete', 'Move_Element_Up', - 'Move_Element_Down']; + var suffixes = [self.options.i18n[self.options.userLang].deselect.replaceAll(' ','_'), + self.options.i18n[self.options.userLang].nextElement.replaceAll(' ','_'), + self.options.i18n[self.options.userLang].previousElement.replaceAll(' ','_'), + self.options.i18n[self.options.userLang].parentElement.replaceAll(' ','_'), + self.options.i18n[self.options.userLang].firstChild.replaceAll(' ','_'), + self.options.i18n[self.options.userLang].nextSibling.replaceAll(' ','_'), + self.options.i18n[self.options.userLang].previousSibling.replaceAll(' ','_'), + self.options.i18n[self.options.userLang].nextAttribute.replaceAll(' ','_'), + self.options.i18n[self.options.userLang].previousAttribute.replaceAll(' ','_'), + self.options.i18n[self.options.userLang].deleteElement.replaceAll(' ','_'), + self.options.i18n[self.options.userLang].moveElementUp.replaceAll(' ','_'), + self.options.i18n[self.options.userLang].moveElementDown.replaceAll(' ','_')]; var hasSelected = self.guiEditor.selectedElement != null && self.guiEditor.active; $.each(suffixes, function(){ if (hasSelected) @@ -1282,6 +1619,7 @@ AbstractXMLObject.prototype.createElementInput = function (inputID, startingValu var selectionValues = this.objectType.values; input = document.createElement('select'); input.id = inputID; + input.readOnly = !this.editor.options.enableEdit; // Added readonly behaviour input.className = 'xml_select'; appendTarget.appendChild(input); @@ -1301,9 +1639,22 @@ AbstractXMLObject.prototype.createElementInput = function (inputID, startingValu || this.objectType.attribute || this.objectType.cdata || this.objectType.comment){ input = document.createElement('textarea'); input.id = inputID; + input.rows = 1; // added start size of 1 row + input.style.height = '18px'; // Predefined height for 1 row. Will be expanded using jQuery.autosize + input.readOnly = !this.editor.options.enableEdit; // Added readonly behaviour input.className = 'xml_textarea'; // Text areas start out with a space so that the pretty formating won't collapse the field input.value = startingValue? startingValue : " "; + // Set width of generated fields manually. + if (this.objectType.attribute && startingValue) { + var len = startingValue.length * 10; + if (len < 80) { len = 80; } + else if (len > 600) { len = 600; } + if (len != -1) { + input.style.width = len + "px"; + } + } + // End feature of resized fields. appendTarget.appendChild(input); $input = $(input); @@ -1311,7 +1662,7 @@ AbstractXMLObject.prototype.createElementInput = function (inputID, startingValu // Clear out the starting space on first focus. This space is there to prevent field collapsing // on new elements in the text editor view $input.one('focus', function() { - if (self.editor.options.expandingTextAreas) + if (!self.objectType.attribute && self.editor.options.expandingTextAreas) // No autosize when attribute is set $input.autosize(); if (this.value == " ") this.value = ""; @@ -1321,6 +1672,7 @@ AbstractXMLObject.prototype.createElementInput = function (inputID, startingValu input = document.createElement('input'); input.type = 'date'; input.id = inputID; + input.readOnly = !this.editor.options.enableEdit; // Added readonly mode input.className = 'xml_date'; input.value = startingValue? startingValue : ""; appendTarget.appendChild(input); @@ -1342,6 +1694,7 @@ AbstractXMLObject.prototype.createElementInput = function (inputID, startingValu input.className = 'xml_input'; } input.id = inputID; + input.readOnly = !this.editor.options.enableEdit; // Added readonly mode input.value = startingValue? startingValue : ""; appendTarget.appendChild(input); @@ -1459,32 +1812,32 @@ AddNodeMenu.prototype.populate = function(xmlElement) { } if (xmlElement.allowChildren) { - $("
  • Add Element
  • ").data('xml', { + $("
  • " + this.editor.options.i18n[this.editor.options.userLang].addElement + "
  • ").data('xml', { target : xmlElement, nodeType : "element" }).appendTo(this.menuContent); } if (xmlElement.allowAttributes) { - $("
  • Add Attribute
  • ").data('xml', { + $("
  • " + this.editor.options.i18n[this.editor.options.userLang].addAttribute + "
  • ").data('xml', { target : xmlElement, nodeType : "attribute" }).appendTo(this.menuContent); } - $("
  • Add CDATA
  • ").data('xml', { + $("
  • " + this.editor.options.i18n[this.editor.options.userLang].addCData + "
  • ").data('xml', { target : xmlElement, nodeType : "cdata" }).appendTo(this.menuContent); - $("
  • Add comment
  • ").data('xml', { + $("
  • " + this.editor.options.i18n[this.editor.options.userLang].addComment + "
  • ").data('xml', { target : xmlElement, nodeType : "comment" }).appendTo(this.menuContent); if (xmlElement.objectType.type != null && xmlElement.allowText) { - this.addButton = $("
  • Add text
  • ").attr({ - title : 'Add text' + this.addButton = $("
  • " + this.editor.options.i18n[this.editor.options.userLang].addText + "
  • ").attr({ + title : this.editor.options.i18n[this.editor.options.userLang].addText }).data('xml', { target : xmlElement, nodeType : "text" @@ -1558,7 +1911,7 @@ AttributeMenu.prototype.populate = function (xmlElement) { var attrName = nsPrefix + attribute.localName; var addButton = $("
  • ").attr({ - title : 'Add ' + attrName, + title : self.editor.options.i18n[self.editor.options.userLang].add + attrName, 'id' : xmlElement.domNodeID + "_" + attrName.replace(":", "_") + "_add" }).html(attrName) .data('xml', { @@ -1661,9 +2014,9 @@ DocumentState.prototype.unsyncedChangeEvent = function() { DocumentState.prototype.updateStateMessage = function () { if (this.isChanged()) { - $("." + submissionStatusClass).html("Unsaved changes"); + $("." + submissionStatusClass).html(this.editor.options.i18n[this.editor.options.userLang].unsavedChanges); } else { - $("." + submissionStatusClass).html("All changes saved"); + $("." + submissionStatusClass).html(this.editor.options.i18n[this.editor.options.userLang].savedChanges); } }; @@ -1772,9 +2125,9 @@ DocumentState.prototype.setXMLFromString = function(xmlString) { xmlDoc.async = false; xmlDoc.loadXML(xmlString); if (xmlDoc.parseError.errorCode != 0) { - throw new Error("Error in line " + xmlDoc.parseError.line + " position " + xmlDoc.parseError.linePos - + "\nError Code: " + xmlDoc.parseError.errorCode + "\nError Reason: " - + xmlDoc.parseError.reason + "Error Line: " + xmlDoc.parseError.srcText); + throw new Error(this.options.i18n[this.options.userLang].xmlDocErrorInLine + xmlDoc.parseError.line + this.options.i18n[this.options.userLang].xmlDocErrorAtPos + xmlDoc.parseError.linePos + + "\n" + this.options.i18n[this.options.userLang].xmlDocErrorCode + xmlDoc.parseError.errorCode + "\n" + this.options.i18n[this.options.userLang].xmlDocErrorReason + + xmlDoc.parseError.reason + this.options.i18n[this.options.userLang].xmlDocErrorLine + xmlDoc.parseError.srcText); } } @@ -1897,7 +2250,7 @@ function GUIEditor(editor) { GUIEditor.prototype.initialize = function(parentContainer) { this.xmlContent = $("
    "); this.xmlContent.data("xml", {}); - this.placeholder = $("
    ").attr("class", "placeholder").html("There are no elements in this document. Use the menu on the right to add new top level elements.") + this.placeholder = $("
    ").attr("class", "placeholder").html(this.editor.options.i18n[this.editor.options.userLang].noElements) .appendTo(this.xmlContent); this.guiContent = $("
    ").attr({'id' : guiContentClass + this.editor.instanceNumber, 'class' : guiContentClass}).appendTo(parentContainer); @@ -2088,8 +2441,10 @@ GUIEditor.prototype.addAttributeEvent = function(parentElement, attribute, addBu parentElement.updated({action : 'attributeAdded', target : attribute.objectType.name}); this.focusObject(attribute.domNode); attribute.select(); - addButton.addClass("disabled"); - attribute.addButton = addButton; + if (addButton) { // Ensure this also works, when there was an event not from the addButton + addButton.addClass("disabled"); + attribute.addButton = addButton; + } this.editor.xmlState.documentChangedEvent(); this.editor.resize(); }; @@ -2464,147 +2819,169 @@ function MenuBar(editor) { // Default menu entries this.headerMenuData = [ { - label : 'File', + label : self.editor.options.i18n[self.editor.options.userLang].file, enabled : true, + show: true, action : function(event) {self.activateMenu(event);}, items : [ { - label : 'Submit to Server', + label : self.editor.options.i18n[self.editor.options.userLang].submitChanges, enabled : defaultSubmitConfig != null, - binding : "ctrl+alt+s", + show: self.editor.options.enableEdit, + binding : self.editor.options.i18n[self.editor.options.userLang].altShiftS, action : function() { self.editor.uploadXML.call(self.editor, defaultSubmitConfig); } }, { - label : 'Export', + label : self.editor.options.i18n[self.editor.options.userLang].export, enabled : (typeof(Blob) !== undefined), - binding : "ctrl+alt+e", + show: true, + binding : self.editor.options.i18n[self.editor.options.userLang].altShiftE, action : $.proxy(self.editor.exportXML, self.editor) } ] }, { - label : 'Edit', - enabled : true, + label : self.editor.options.i18n[self.editor.options.userLang].edit, + enabled : self.editor.options.enableEdit, // readonly mode + show: self.editor.options.enableEdit, // readonly mode action : function(event) {self.activateMenu(event);}, items : [ { - label : 'Undo', - enabled : false, - binding : "ctrl+z or mac+z", + label : self.editor.options.i18n[self.editor.options.userLang].undoMenuitem, + enabled : true, + show: true, + binding : self.editor.options.i18n[self.editor.options.userLang].undo, action : function() { self.editor.undoHistory.changeHead(-1); } }, { - label : 'Redo', - enabled : false, - binding : "ctrl+y or mac+shift+z", + label : self.editor.options.i18n[self.editor.options.userLang].redoMenuitem, + enabled : true, + show: true, + binding : self.editor.options.i18n[self.editor.options.userLang].redo, action : function() { self.editor.undoHistory.changeHead(1); } }, { - label : 'Delete', + label : self.editor.options.i18n[self.editor.options.userLang].deleteElement, enabled : true, - binding : "del", + show: true, + binding : self.editor.options.i18n[self.editor.options.userLang].del, action : function(){ self.editor.guiEditor.deleteSelected(); } }, { - label : 'Move Element Up', + label : self.editor.options.i18n[self.editor.options.userLang].moveElementUp, enabled : true, - binding : "alt+up", + show: true, + binding : self.editor.options.i18n[self.editor.options.userLang].elementUp, action : function(){ self.editor.guiEditor.moveSelected(true); } }, { - label : 'Move Element Down', + label : self.editor.options.i18n[self.editor.options.userLang].moveElementDown, enabled : true, - binding : "alt+down", + show: true, + binding : self.editor.options.i18n[self.editor.options.userLang].elementDown, action : function(){ self.editor.guiEditor.moveSelected(); } } ] }, { - label : 'Select', - enabled : true, + label : self.editor.options.i18n[self.editor.options.userLang].select, + enabled : self.editor.options.enableEdit, // readonly mode + show: self.editor.options.enableEdit, // readonly mode action : function(event) {self.activateMenu(event);}, items : [ { - label : 'Deselect', + label : self.editor.options.i18n[self.editor.options.userLang].deselect, enabled : true, - binding : "esc", + show: true, + binding : self.editor.options.i18n[self.editor.options.userLang].esc, action : function(){ self.editor.guiEditor.deselect(); } },{ - label : 'Next Element', + label : self.editor.options.i18n[self.editor.options.userLang].nextElement, enabled : true, - binding : "down", + show: true, + binding : self.editor.options.i18n[self.editor.options.userLang].down, action : function(){ self.editor.guiEditor.selectNext(); } }, { - label : 'Previous Element', + label : self.editor.options.i18n[self.editor.options.userLang].previousElement, enabled : true, - binding : "up", + show: true, + binding : self.editor.options.i18n[self.editor.options.userLang].up, action : function(){ self.editor.guiEditor.selectNext(true); } }, { - label : 'Next Attribute', + label : self.editor.options.i18n[self.editor.options.userLang].nextAttribute, enabled : true, - binding : "right", + show: true, + binding : self.editor.options.i18n[self.editor.options.userLang].right, action : function(){ self.editor.guiEditor.selectAttribute(); } }, { - label : 'Previous Attribute', + label : self.editor.options.i18n[self.editor.options.userLang].previousAttribute, enabled : true, - binding : "left", + show: true, + binding : self.editor.options.i18n[self.editor.options.userLang].left, action : function(){ self.editor.guiEditor.selectAttribute(true); } }, { - label : 'Parent', + label : self.editor.options.i18n[self.editor.options.userLang].parentElement, enabled : true, - binding : "shift+left", + show: true, + binding : self.editor.options.i18n[self.editor.options.userLang].shiftLeft, action : function(){ self.editor.guiEditor.selectParent(); } }, { - label : 'First Child', + label : self.editor.options.i18n[self.editor.options.userLang].firstChild, enabled : true, - binding : "shift+right", + show: true, + binding : self.editor.options.i18n[self.editor.options.userLang].shiftRight, action : function(){ self.editor.guiEditor.selectParent(true); } }, { - label : 'Next Sibling', + label : self.editor.options.i18n[self.editor.options.userLang].nextSibling, enabled : true, - binding : "shift+down", + show: true, + binding : self.editor.options.i18n[self.editor.options.userLang].shiftDown, action : function(){ self.editor.guiEditor.selectSibling(); } }, { - label : 'Previous Sibling', + label : self.editor.options.i18n[self.editor.options.userLang].previousSibling, enabled : true, - binding : "shift+up", + show: true, + binding : self.editor.options.i18n[self.editor.options.userLang].shiftUp, action : function(){ self.editor.guiEditor.selectSibling(true); } } ] }, { - label : 'Insert', + label : self.editor.options.i18n[self.editor.options.userLang].insert, enabled : true, + show: self.editor.options.enableEdit, action : function(event) {self.activateMenu(event);}, items : [ { - label : 'Add attribute', + label : self.editor.options.i18n[self.editor.options.userLang].addAttribute, enabled : true, - binding : "alt+a", + show: true, + binding : self.editor.options.i18n[self.editor.options.userLang].altA, action : function(){ var selected = self.editor.guiEditor.selectedElement; if (selected instanceof XMLElement) self.editor.addNode(selected, "attribute", false); } }, { - label : 'Add element', + label : self.editor.options.i18n[self.editor.options.userLang].addElement, enabled : true, - binding : "enter", + show: true, + binding : self.editor.options.i18n[self.editor.options.userLang].enter, action : function(){ var selected = self.editor.guiEditor.selectedElement; if (selected instanceof XMLElement) { @@ -2612,61 +2989,68 @@ function MenuBar(editor) { } } }, { - label : 'Add child element', + label : self.editor.options.i18n[self.editor.options.userLang].addChildElement, enabled : true, - binding : "alt+e", + show: true, + binding : self.editor.options.i18n[self.editor.options.userLang].addE, action : function(){ var selected = self.editor.guiEditor.selectedElement; if (selected instanceof XMLElement) self.editor.addNode(selected, "element", false); } }, { - label : 'Add sibling element', + label : self.editor.options.i18n[self.editor.options.userLang].addSiblingElement, enabled : true, - binding : "alt+s", + show: true, + binding : self.editor.options.i18n[self.editor.options.userLang].addS, action : function(){ var selected = self.editor.guiEditor.selectedElement; if (selected) self.editor.addNode(selected.parentElement, "element", false, selected); } }, { - label : 'Add element to parent', + label : self.editor.options.i18n[self.editor.options.userLang].addElementToParent, enabled : true, - binding : "alt+p", + show: true, + binding : self.editor.options.i18n[self.editor.options.userLang].addP, action : function(){ var selected = self.editor.guiEditor.selectedElement; if (selected) self.editor.addNode(selected.parentElement, "element", false); } }, { - label : 'Add element to root', + label : self.editor.options.i18n[self.editor.options.userLang].addElementToRoot, enabled : true, - binding : "alt+r", + show: true, + binding : self.editor.options.i18n[self.editor.options.userLang].altR, action : function(){ self.editor.addNode(self.editor.guiEditor.rootElement, "element", false); } }, { - label : 'Add text to element', + label : self.editor.options.i18n[self.editor.options.userLang].addTextToElement, enabled : true, - binding : "alt+t", + show: true, + binding : self.editor.options.i18n[self.editor.options.userLang].altT, action : function(){ var selected = self.editor.guiEditor.selectedElement; if (selected) self.editor.addNode(selected, "text", false); } }, { - label : 'Add comment to element', + label : self.editor.options.i18n[self.editor.options.userLang].addCommentToElement, enabled : true, - binding : "alt+/", + show: true, + binding : self.editor.options.i18n[self.editor.options.userLang].altSlash, action : function(){ var selected = self.editor.guiEditor.selectedElement; if (selected) self.editor.addNode(selected, "comment", false); } }, { - label : 'Add CDATA to element', + label : self.editor.options.i18n[self.editor.options.userLang].addCDataToElement, enabled : true, - binding : "alt+,", + show: true, + binding : self.editor.options.i18n[self.editor.options.userLang].altComma, action : function(){ var selected = self.editor.guiEditor.selectedElement; if (selected) @@ -2674,47 +3058,52 @@ function MenuBar(editor) { } } ] }, { - label : 'View', + label : self.editor.options.i18n[self.editor.options.userLang].view, enabled : true, + show: true, action : function(event) {self.activateMenu(event);}, items : [ { - label : 'Switch to XML View', + label : self.editor.options.i18n[self.editor.options.userLang].switchToXml, enabled : true, - binding : "ctrl+alt+1", + binding : self.editor.options.i18n[self.editor.options.userLang].altShiftX, action : function() { self.editor.modeChange(0); } }, { - label : 'Switch to Text View', + label : self.editor.options.i18n[self.editor.options.userLang].switchToText, enabled : true, - binding : "ctrl+alt+2", + binding : self.editor.options.i18n[self.editor.options.userLang].altShiftT, action : function() { self.editor.modeChange(1); } } ] }, { - label : 'Options', + label : self.editor.options.i18n[self.editor.options.userLang].options, enabled : true, + show: true, action : function(event) {self.activateMenu(event);}, items : [ { - label : 'Pretty XML Formatting', + label : self.editor.options.i18n[self.editor.options.userLang].prettifyXml, enabled : true, + show: true, checked : self.editor.options.prettyXML, action : function() { self.editor.options.prettyXML = !self.editor.options.prettyXML; self.checkEntry(this, self.editor.options.prettyXML); } }, { - label : 'Enable shortcut keys', + label : self.editor.options.i18n[self.editor.options.userLang].enableShortcuts, enabled : true, + show: true, checked : self.editor.options.enableGUIKeybindings, action : function() { self.editor.setEnableKeybindings(!self.editor.options.enableGUIKeybindings); self.checkEntry(this, self.editor.options.enableGUIKeybindings); } }, { - label : 'Enforce min/max occurs', + label : self.editor.options.i18n[self.editor.options.userLang].enforceMinMaxOccurs, enabled : self.editor.options.enforceOccurs, + show: true, checked : self.editor.options.enforceOccurs, action : function() { self.editor.options.enforceOccurs = !self.editor.options.enforceOccurs; @@ -2722,8 +3111,9 @@ function MenuBar(editor) { self.checkEntry(this, self.editor.options.enforceOccurs); } }, { - label : 'Prepend new elements', + label : self.editor.options.i18n[self.editor.options.userLang].prependNewElements, enabled : true, + show: true, checked : self.editor.options.prependNewElements, action : function() { self.editor.options.prependNewElements = !self.editor.options.prependNewElements; @@ -2741,15 +3131,17 @@ function MenuBar(editor) { action : "http://www.loc.gov/standards/mods/mods-outline.html" } ] }*/, { - label : self.editor.options.xmlEditorLabel, + label : self.editor.options.i18n[self.editor.options.userLang].xml, enabled : true, + show: true, itemClass : 'header_mode_tab', action : function() { self.editor.modeChange(0); } }, { - label : self.editor.options.textEditorLabel, + label : self.editor.options.i18n[self.editor.options.userLang].text, enabled : true, + show: true, itemClass : 'header_mode_tab', action : function() { self.editor.modeChange(1); @@ -2777,16 +3169,20 @@ MenuBar.prototype.activateMenu = function(event) { // Builds the menu and attaches it to the editor MenuBar.prototype.render = function(parentElement) { this.parentElement = parentElement; - this.menuBarContainer = $("
    ").addClass(xmlMenuBarClass).appendTo(parentElement); + if (this.editor.options.sourceDesignSwitch) {// Enable hiding the XML/Source switch buttons + this.menuBarContainer = $("
    ").addClass(xmlMenuBarClass).appendTo(parentElement); - this.headerMenu = $("
      "); - this.menuBarContainer.append(this.headerMenu); - this.initEventHandlers(); + this.headerMenu = $("
        "); + this.menuBarContainer.append(this.headerMenu); + this.initEventHandlers(); - var menuBar = this; - $.each(this.headerMenuData, function() { - menuBar.generateMenuItem(this, menuBar.headerMenu); - }); + var menuBar = this; + $.each(this.headerMenuData, function() { + if (this.show) { // Enable hiding unwanted menus + menuBar.generateMenuItem(this, menuBar.headerMenu); + } + }); + } }; MenuBar.prototype.initEventHandlers = function() { @@ -3031,16 +3427,17 @@ function ModifyMenuPanel(editor) { } ModifyMenuPanel.prototype.initialize = function (parentContainer) { - this.menuColumn = $("
        ").attr('class', menuColumnClass).appendTo(parentContainer); + if (this.editor.options.enableEdit) // Enable edit switch does control the visibility of some elements. + this.menuColumn = $("
        ").attr('class', menuColumnClass).appendTo(parentContainer); // Generate the document status panel, which shows a save/export button as well as if there are changes to the document if (this.editor.options.enableDocumentStatusPanel) { var self = this; var documentStatusPanel = $(self.editor.options.documentStatusPanelDomId); - $("").addClass(submissionStatusClass).html("Document is unchanged") + $("").addClass(submissionStatusClass).html(self.editor.options.i18n[self.editor.options.userLang].documentUnchanged) .appendTo(documentStatusPanel); - if (self.editor.submitButtonConfigs != null){ + if (self.editor.options.showExport && self.editor.submitButtonConfigs != null){ $.each(self.editor.submitButtonConfigs, function(index, config){ var submitButton; if (config.id && ('createDomElement' in config) && !config.createDomElement) { @@ -3374,6 +3771,7 @@ TextEditor.prototype.initialize = function(parentContainer) { this.xmlContent = $("
        ").attr({'id' : textContentClass + this.editor.instanceNumber, 'class' : textContentClass}).appendTo(parentContainer); this.xmlEditorDiv = $("
        ").attr('id', 'text_editor').appendTo(this.xmlContent); this.aceEditor = ace.edit("text_editor"); + this.aceEditor.setReadOnly(!this.editor.options.enableEdit); // readonly for the ace editor this.aceEditor.setTheme("ace/theme/textmate"); this.aceEditor.getSession().setMode("ace/mode/xml"); this.aceEditor.setShowPrintMargin(false); @@ -3497,10 +3895,14 @@ TextEditor.prototype.resize = function() { this.xmlContent.css({'height': xmlEditorHeight + 'px'}); this.xmlEditorDiv.width(this.xmlContent.innerWidth()); this.xmlEditorDiv.height(xmlEditorHeight); - if (this.editor.modifyMenu.menuContainer != null){ + if (this.editor.modifyMenu.menuContainer != null && this.editor.modifyMenu.menuContainer.offset() != null){ this.editor.modifyMenu.menuContainer.css({ 'max-height': $(this.editor.xmlWorkAreaContainer).height() - this.editor.modifyMenu.menuContainer.offset().top }); + } else { + this.editor.modifyMenu.menuContainer.css({ + 'max-height': $(this.editor.xmlWorkAreaContainer).height() - 10 + }); } if (this.aceEditor != null) this.aceEditor.resize(); @@ -3768,10 +4170,11 @@ XMLAttribute.prototype.render = function (){ }).data('xmlAttribute', this).appendTo(this.xmlElement.getAttributeContainer()); var self = this; - var removeButton = document.createElement('a'); - removeButton.appendChild(document.createTextNode('(x) ')); - this.domNode[0].appendChild(removeButton); - + if ("required" != this.objectType.use && this.editor.options.enableEdit) { // Don't add required attributes in readonly mode + var removeButton = document.createElement('a'); + removeButton.appendChild(document.createTextNode('(x) ')); + this.domNode[0].appendChild(removeButton); + } var label = document.createElement('label'); var prefix = this.editor.xmlState.namespaces.getNamespacePrefix(this.objectType.namespace); label.appendChild(document.createTextNode(prefix + this.objectType.localName)); @@ -4324,6 +4727,14 @@ XMLElement.prototype.populateChildren = function() { } } }); + // Patch to add "required" attributes. Does require a secondary change to know the "use" attribute also. See xsd2json-use.js : SchemaProcessor.prototype.createDefinition + $.each(this.objectType.attributes, function(){ + if ("required" == this.use) { + var childElement = self.addAttribute(this); + // Needs a patch in addAttributeEvent, too -> non-provided addButton parameter must be handled. + self.editor.activeEditor.addAttributeEvent(self, childElement); + } + }); }; XMLElement.prototype.initializeGUI = function () { @@ -4352,24 +4763,25 @@ XMLElement.prototype.addTopActions = function () { this.toggleCollapse.appendChild(document.createTextNode('_')); topActionSpan.appendChild(this.toggleCollapse); - var moveDown = document.createElement('span'); - moveDown.className = 'move_down'; - moveDown.id = this.domNodeID + '_down'; - moveDown.appendChild(document.createTextNode('\u2193')); - topActionSpan.appendChild(moveDown); - - var moveUp = document.createElement('span'); - moveUp.className = 'move_up'; - moveUp.id = this.domNodeID + '_up'; - moveUp.appendChild(document.createTextNode('\u2191')); - topActionSpan.appendChild(moveUp); - - var deleteButton = document.createElement('span'); - deleteButton.className = 'xml_delete'; - deleteButton.id = this.domNodeID + '_del'; - deleteButton.appendChild(document.createTextNode('X')); - topActionSpan.appendChild(deleteButton); - + if (this.editor.options.enableEdit) { // Only show menu entries in edit mode, not readonly mode + var moveDown = document.createElement('span'); + moveDown.className = 'move_down'; + moveDown.id = this.domNodeID + '_down'; + moveDown.appendChild(document.createTextNode('\u2193')); + topActionSpan.appendChild(moveDown); + + var moveUp = document.createElement('span'); + moveUp.className = 'move_up'; + moveUp.id = this.domNodeID + '_up'; + moveUp.appendChild(document.createTextNode('\u2191')); + topActionSpan.appendChild(moveUp); + + var deleteButton = document.createElement('span'); + deleteButton.className = 'xml_delete'; + deleteButton.id = this.domNodeID + '_del'; + deleteButton.appendChild(document.createTextNode('X')); + topActionSpan.appendChild(deleteButton); + } return topActionSpan; }; @@ -4388,24 +4800,24 @@ XMLElement.prototype.addContentContainers = function (recursive) { if (this.allowText) { if (this.allowAttributes) { if (this.allowChildren) { - placeholder.appendChild(document.createTextNode('Use the menu to add subelements, attributes and text.')); + placeholder.appendChild(document.createTextNode(this.editor.options.i18n[this.editor.options.userLang].useMenuToAdd)); } else { - placeholder.appendChild(document.createTextNode('Use the menu to add attributes and text.')); + placeholder.appendChild(document.createTextNode(this.editor.options.i18n[this.editor.options.userLang].useMenuToAddAttributesText)); } } else if (this.allowChildren) { - placeholder.appendChild(document.createTextNode('Use the menu to add subelements and text.')); + placeholder.appendChild(document.createTextNode(this.editor.options.i18n[this.editor.options.userLang].useMenuToAddSubelementsText)); } else { - placeholder.appendChild(document.createTextNode('Use the menu to add text.')); + placeholder.appendChild(document.createTextNode(this.editor.options.i18n[this.editor.options.userLang].useMenuToAddText)); } } else { if (this.allowAttributes) { if (this.allowChildren) { - placeholder.appendChild(document.createTextNode('Use the menu to add subelements and attributes.')); + placeholder.appendChild(document.createTextNode(this.editor.options.i18n[this.editor.options.userLang].useMenuToAddSubelementsAttributes)); } else { - placeholder.appendChild(document.createTextNode('Use the menu to add attributes.')); + placeholder.appendChild(document.createTextNode(this.editor.options.i18n[this.editor.options.userLang].useMenuToAddAttributes)); } } else if (this.allowChildren) { - placeholder.appendChild(document.createTextNode('Use the menu to add subelements.')); + placeholder.appendChild(document.createTextNode(this.editor.options.i18n[this.editor.options.userLang].useMenuToAddSubelements)); } } @@ -5007,9 +5419,9 @@ XMLTemplates.prototype.createDialog = function() { var self = this; var buttons = {}; if (self.editor.options.templateOptions.cancelFunction) { - buttons["Cancel"] = $.proxy(self.editor.options.templateOptions.cancelFunction, self); + buttons[self.editor.options.i18n[self.editor.options.userLang].cancel] = $.proxy(self.editor.options.templateOptions.cancelFunction, self); } - buttons["Choose"] = function() { + buttons[self.editor.options.i18n[self.editor.options.userLang].choose] = function() { self.processForm(); }; @@ -5093,7 +5505,7 @@ XMLTemplates.prototype.loadSelectedTemplate = function(selection) { var xml_string = self.editor.xml2Str(data); self.editor._documentReady(xml_string); }).fail(function(jqXHR, textStatus) { - alert("Unable to load the requested template: " + textStatus); + alert(self.editor.options.i18n[self.editor.options.userLang].unableToLoadTemplate + textStatus); }); }; @@ -5258,11 +5670,11 @@ XMLTextNode.prototype.focus = function() { XMLTextNode.prototype.isSelected = function() { return AbstractXMLObject.prototype.isSelected.call(this); }; + /** * Stores data related to a single xml element as it is represented in both the base XML * document and GUI */ - function XMLUnspecifiedElement(xmlNode, editor) { var unspecifiedType = { element : true, @@ -5349,7 +5761,7 @@ XMLUnspecifiedElement.prototype.addContentContainers = function (recursive) { var placeholder = document.createElement('div'); placeholder.className = 'placeholder'; - placeholder.appendChild(document.createTextNode('Use the menu to add contents.')); + placeholder.appendChild(document.createTextNode(this.editor.options.i18n[this.options.userLang].useMenuToAddContents)); this.contentContainer.appendChild(placeholder); this.placeholder = $(placeholder); @@ -5366,4 +5778,4 @@ XMLUnspecifiedElement.prototype.updateChildrenCount = function(childElement, del XMLUnspecifiedElement.prototype.childCanBeRemoved = function(childType) { return true; }; -})(jQuery); \ No newline at end of file +})(jQuery); diff --git a/src/abstract_xml_object.js b/src/abstract_xml_object.js index 805afe7..25c1f13 100644 --- a/src/abstract_xml_object.js +++ b/src/abstract_xml_object.js @@ -27,6 +27,7 @@ AbstractXMLObject.prototype.createElementInput = function (inputID, startingValu var selectionValues = this.objectType.values; input = document.createElement('select'); input.id = inputID; + input.readOnly = !this.editor.options.enableEdit; // Added readonly behaviour input.className = 'xml_select'; appendTarget.appendChild(input); @@ -46,9 +47,22 @@ AbstractXMLObject.prototype.createElementInput = function (inputID, startingValu || this.objectType.attribute || this.objectType.cdata || this.objectType.comment){ input = document.createElement('textarea'); input.id = inputID; + input.rows = 1; // added start size of 1 row + input.style.height = '18px'; // Predefined height for 1 row. Will be expanded using jQuery.autosize + input.readOnly = !this.editor.options.enableEdit; // Added readonly behaviour input.className = 'xml_textarea'; // Text areas start out with a space so that the pretty formating won't collapse the field input.value = startingValue? startingValue : " "; + // Set width of generated fields manually. + if (this.objectType.attribute && startingValue) { + var len = startingValue.length * 10; + if (len < 80) { len = 80; } + else if (len > 600) { len = 600; } + if (len != -1) { + input.style.width = len + "px"; + } + } + // End feature of resized fields. appendTarget.appendChild(input); $input = $(input); @@ -56,7 +70,7 @@ AbstractXMLObject.prototype.createElementInput = function (inputID, startingValu // Clear out the starting space on first focus. This space is there to prevent field collapsing // on new elements in the text editor view $input.one('focus', function() { - if (self.editor.options.expandingTextAreas) + if (!self.objectType.attribute && self.editor.options.expandingTextAreas) // No autosize when attribute is set $input.autosize(); if (this.value == " ") this.value = ""; @@ -66,6 +80,7 @@ AbstractXMLObject.prototype.createElementInput = function (inputID, startingValu input = document.createElement('input'); input.type = 'date'; input.id = inputID; + input.readOnly = !this.editor.options.enableEdit; // Added readonly mode input.className = 'xml_date'; input.value = startingValue? startingValue : ""; appendTarget.appendChild(input); @@ -87,6 +102,7 @@ AbstractXMLObject.prototype.createElementInput = function (inputID, startingValu input.className = 'xml_input'; } input.id = inputID; + input.readOnly = !this.editor.options.enableEdit; // Added readonly mode input.value = startingValue? startingValue : ""; appendTarget.appendChild(input); @@ -159,4 +175,4 @@ AbstractXMLObject.prototype.select = function() { AbstractXMLObject.prototype.isSelected = function() { return this.domNode.hasClass("selected"); -}; \ No newline at end of file +}; diff --git a/src/add_node_menu.js b/src/add_node_menu.js index ede0b86..a89a625 100644 --- a/src/add_node_menu.js +++ b/src/add_node_menu.js @@ -42,32 +42,32 @@ AddNodeMenu.prototype.populate = function(xmlElement) { } if (xmlElement.allowChildren) { - $("
      • Add Element
      • ").data('xml', { + $("
      • " + this.editor.options.i18n[this.editor.options.userLang].addElement + "
      • ").data('xml', { target : xmlElement, nodeType : "element" }).appendTo(this.menuContent); } if (xmlElement.allowAttributes) { - $("
      • Add Attribute
      • ").data('xml', { + $("
      • " + this.editor.options.i18n[this.editor.options.userLang].addAttribute + "
      • ").data('xml', { target : xmlElement, nodeType : "attribute" }).appendTo(this.menuContent); } - $("
      • Add CDATA
      • ").data('xml', { + $("
      • " + this.editor.options.i18n[this.editor.options.userLang].addCData + "
      • ").data('xml', { target : xmlElement, nodeType : "cdata" }).appendTo(this.menuContent); - $("
      • Add comment
      • ").data('xml', { + $("
      • " + this.editor.options.i18n[this.editor.options.userLang].addComment + "
      • ").data('xml', { target : xmlElement, nodeType : "comment" }).appendTo(this.menuContent); if (xmlElement.objectType.type != null && xmlElement.allowText) { - this.addButton = $("
      • Add text
      • ").attr({ - title : 'Add text' + this.addButton = $("
      • " + this.editor.options.i18n[this.editor.options.userLang].addText + "
      • ").attr({ + title : this.editor.options.i18n[this.editor.options.userLang].addText }).data('xml', { target : xmlElement, nodeType : "text" @@ -94,4 +94,4 @@ AddNodeMenu.prototype.populate = function(xmlElement) { AddNodeMenu.prototype.clear = function() { this.menuContent.hide(); -}; \ No newline at end of file +}; diff --git a/src/attribute_menu.js b/src/attribute_menu.js index 754246a..306cfdd 100644 --- a/src/attribute_menu.js +++ b/src/attribute_menu.js @@ -44,7 +44,7 @@ AttributeMenu.prototype.populate = function (xmlElement) { var attrName = nsPrefix + attribute.localName; var addButton = $("
      • ").attr({ - title : 'Add ' + attrName, + title : self.editor.options.i18n[self.editor.options.userLang].add + attrName, 'id' : xmlElement.domNodeID + "_" + attrName.replace(":", "_") + "_add" }).html(attrName) .data('xml', { diff --git a/src/document_state.js b/src/document_state.js index 240d33a..500ea98 100644 --- a/src/document_state.js +++ b/src/document_state.js @@ -1,6 +1,7 @@ /** * Manages and tracks the state of the underlying XML document being edited. */ + function DocumentState(baseXML, editor) { this.baseXML = baseXML; this.xml = null; @@ -67,9 +68,9 @@ DocumentState.prototype.unsyncedChangeEvent = function() { DocumentState.prototype.updateStateMessage = function () { if (this.isChanged()) { - $("." + submissionStatusClass).html("Unsaved changes"); + $("." + submissionStatusClass).html(this.editor.options.i18n[this.editor.options.userLang].unsavedChanges); } else { - $("." + submissionStatusClass).html("All changes saved"); + $("." + submissionStatusClass).html(this.editor.options.i18n[this.editor.options.userLang].savedChanges); } }; @@ -178,9 +179,9 @@ DocumentState.prototype.setXMLFromString = function(xmlString) { xmlDoc.async = false; xmlDoc.loadXML(xmlString); if (xmlDoc.parseError.errorCode != 0) { - throw new Error("Error in line " + xmlDoc.parseError.line + " position " + xmlDoc.parseError.linePos - + "\nError Code: " + xmlDoc.parseError.errorCode + "\nError Reason: " - + xmlDoc.parseError.reason + "Error Line: " + xmlDoc.parseError.srcText); + throw new Error(this.options.i18n[this.options.userLang].xmlDocErrorInLine + xmlDoc.parseError.line + this.options.i18n[this.options.userLang].xmlDocErrorAtPos + xmlDoc.parseError.linePos + + "\n" + this.options.i18n[this.options.userLang].xmlDocErrorCode + xmlDoc.parseError.errorCode + "\n" + this.options.i18n[this.options.userLang].xmlDocErrorReason + + xmlDoc.parseError.reason + this.options.i18n[this.options.userLang].xmlDocErrorLine + xmlDoc.parseError.srcText); } } diff --git a/src/format_xml.js b/src/format_xml.js index b5cbf5f..70ad180 100644 --- a/src/format_xml.js +++ b/src/format_xml.js @@ -95,4 +95,5 @@ function formatXML(element, indent, options) { return contents; } -} \ No newline at end of file +} +; diff --git a/src/gui_editor.js b/src/gui_editor.js index d2a0c3d..20ffaca 100644 --- a/src/gui_editor.js +++ b/src/gui_editor.js @@ -1,6 +1,7 @@ /** * Graphical editor */ + function GUIEditor(editor) { this.editor = editor; this.guiContent = null; @@ -13,7 +14,7 @@ function GUIEditor(editor) { GUIEditor.prototype.initialize = function(parentContainer) { this.xmlContent = $("
        "); this.xmlContent.data("xml", {}); - this.placeholder = $("
        ").attr("class", "placeholder").html("There are no elements in this document. Use the menu on the right to add new top level elements.") + this.placeholder = $("
        ").attr("class", "placeholder").html(this.editor.options.i18n[this.editor.options.userLang].noElements) .appendTo(this.xmlContent); this.guiContent = $("
        ").attr({'id' : guiContentClass + this.editor.instanceNumber, 'class' : guiContentClass}).appendTo(parentContainer); @@ -204,8 +205,10 @@ GUIEditor.prototype.addAttributeEvent = function(parentElement, attribute, addBu parentElement.updated({action : 'attributeAdded', target : attribute.objectType.name}); this.focusObject(attribute.domNode); attribute.select(); - addButton.addClass("disabled"); - attribute.addButton = addButton; + if (addButton) { // Ensure this also works, when there was an event not from the addButton + addButton.addClass("disabled"); + attribute.addButton = addButton; + } this.editor.xmlState.documentChangedEvent(); this.editor.resize(); }; diff --git a/src/jquery.xmleditor.js b/src/jquery.xmleditor.js index 6dabdad..b0a0557 100644 --- a/src/jquery.xmleditor.js +++ b/src/jquery.xmleditor.js @@ -74,6 +74,30 @@ var localName = function(node) { return node.nodeName.substring(index + 1); }; +// Extracts and returns URL parameters or the given default. +function getParam(name, def) { + name = name.replace(/[\[]/, "\\[").replace(/[\]]/, "\\]"); + var regex = new RegExp("[\\?&]" + name + "=([^&#]*)"), + results = regex.exec(location.search); + return results === null ? def : decodeURIComponent(results[1].replace(/\+/g, " ")); +} + +// Add method to String. +String.prototype.replaceAll = function(search, replace) { + //if replace is null, return original string otherwise it will + //replace search string with 'undefined'. + if(!replace) + return this; + return this.replace(new RegExp('[' + search + ']', 'g'), replace); +}; + +// Extract the user language first from the browsers default and let it overwrite via URL parameter "lang=". +var userLang = navigator.language || navigator.userLanguage; +userLang = getParam("lang", userLang); +if ("de" != userLang && "en" != userLang) { + userLang = "en"; +} + $.widget( "xml.xmlEditor", { options: { // Schema object to be used @@ -106,11 +130,6 @@ $.widget( "xml.xmlEditor", { elementUpdated : undefined, // Title for the document, displayed in the header documentTitle : null, - addTopMenuHeaderText : 'Add Top Element', - addAttrMenuHeaderText : 'Add Attribute', - addElementMenuHeaderText : 'Add Subelement', - xmlEditorLabel : 'XML', - textEditorLabel : 'Text', // Set to false to get rid of the enableDocumentStatusPanel : true, @@ -131,7 +150,276 @@ $.widget( "xml.xmlEditor", { prependNewElements: false, autocomplete: true, - targetNS: null + targetNS: null, + + // Flag to disable the export button + showExport: true, + + // Switch to readonly mode + enableEdit: true, + + // Hide the ability to switch between the modes + sourceDesignSwitch: true, + + // Switch to predefined view: XML or HTML-DIV editor. 0=HTML-DIV editor, 1=XML Source editor + initialEditMode: 0, + + // i18n resources + i18n: { + "en": { + uploadedFiles:"Uploaded files:", + addNodes:"Add nodes", + addRoot:"Add top element", + addAttribute:"Add attribute", + addElement:"Add Element", + addSubelement:"Add subelement", + insert:"Insert", + xml:"Design", + text:"Source", + editing:"Editing ", + noBrowserExportSupport:"Browser does not support saving files via this editor. To save, copy and paste the document from the Text view.", + filename:"Filename", + saveFailedXmlInvalid:"The XML is not valid. The content cannot be processed.", + saveFailedSeeErrors:"Save failed
        See error message.", + export:"Export", + download:"Download", + submitting:"Processing...", + failedToSubmit:"The XML document could not be successfully submitted", + documentUnchanged:"No changes", + submitChanges:"Apply", + noElements:"There are no elements in this document. Use the menu on the right to add new top level elements.", + unsavedChanges:"Unapplied changes", + savedChanges:"Changes applied.", + couldNotAdd1:"Could not add child ", + couldNotAdd2:", it is not a valid child of ", + couldNotAddAttr1:"Could not add attribute ", + couldNotAddAttr2:", it is not a valid for element ", + noDocument:"Could not load specified document and no fallback provided, cannot start.", + noStartingDocument:"No starting document.", + failedToAddChild:"Failed to add child of type ", + undo:"ctrl+z", + redo:"ctrl+y", + del:"del", + enter:"enter", + elementUp:"alt+up", + elementDown:"alt+down", + esc:"esc", + down:"down", + up:"up", + left:"left", + right:"right", + shiftDown:"shift+down", + shiftUp:"shift+up", + shiftLeft:"shift+left", + shiftRight:"shift+right", + altA:"alt+a", + ltShiftE:"alt+shift+e", + altShiftX:"alt+shift+x", + altShiftS:"alt+shift+s", + altShiftT:"alt+shift+t", + file:"File", + edit:"Edit", + select:"Select", + view:"View", + options:"Options", + deselect:"Deselect", + nextElement:"Next Element", + previousElement:"Previous Element", + parentElement:"Parent", + firstChild:"First Child", + nextSibling:"Next Sibling", + previousSibling:"Previous Sibling", + nextAttribute:"Next Attribute", + previousAttribute:"Previous Attribute", + deleteElement:"Delete", + moveElementUp:"Move up", + moveElementDown:"Move down", + undoMenuitem:"Undo", + redoMenuitem:"Redo", + switchToXml:"Graphical XML", + switchToText:"Raw XML", + prettifyXml:"Automatic XML formatting", + enableShortcuts:"Enable shortcuts", + enforceMinMaxOccurs:"Enforce Min/Max", + prependNewElements:"Prepend adding new elements", + noConnection:"No connection.\nPlease check network.", + pageNotFound:"The requested page could not be found. [404]", + internalServerError:"Internal server error [500].", + jsonParseFailed:"JSON parse failed.", + timeout:"timeout while contacting server.", + ajaxAborted:"AJAX request aborted.", + uncaughtError:"Unexpected error\n", + xmlSerializerNotSupported:"XML Serializer not supported.", + alreadyExistent:"Already existent", + useTheMenuToAddSubElementsAndAttr:"Use the menu to add subelements and attributes.", + useTheMenuToAddAttr:"Use the menu to add attributes.", + useTheMenuToAddSubElements:"Use the menu to add subelements.", + cannotSubmitNoPostOption:"Cannot submit because no post Options", + useMenuToAdd:"Use the menu to add subelements, attributes and text.", + useMenuToAddAttributesText:"Use the menu to add attributes and text.", + useMenuToAddSubelementsText:"Use the menu to add subelements and text.", + useMenuToAddText:"Use the menu to add text.", + useMenuToAddSubelementsAttributes:"Use the menu to add subelements and attributes.", + useMenuToAddAttributes:"Use the menu to add attributes.", + useMenuToAddSubelements:"Use the menu to add subelements.", + unableToAddFixSyntax:"Unable to add element, please fix existing XML syntax first.", + addCData:"Add CDATA", + addComment:"Add Comment", + addText:"Add Text", + add:"Add ", + xmlDocErrorInLine:"Error in line ", + xmlDocErrorAtPos:" position ", + xmlDocErrorCode:"Error Code: ", + xmlDocErrorReason:"Error Reason: ", + xmlDocErrorLine:"Error Line: ", + addChildElement:"Add child element", + addSiblingElement:"Add sibling element", + addElementToParent:"Add element to parent", + addElementToRoot:"Add element to root", + addTextToElement:"Add text to element", + addCommentToElement:"Add comment to element", + addCDataToElement:"Add CDATA to element", + altE:"alt+2", + altS:"alt+s", + altP:"alt+p", + altR:"alt+r", + altT:"alt+t", + altSlash:"alt+/", + altComma:"alt+,", + cancel:"Cancel", + choose:"Choose", + unableToLoadTemplate:"Unable to load the requested template: ", + useMenuToAddContents:"Use the menu to add contents." + }, + "de": { + uploadedFiles:"Hochgeladene Dateien:", + addNodes:"Elemente hinzufügen", + addRoot:"Hauptelement hinzufügen", + addAttribute:"Attribut hinzufügen", + addElement:"Element hinzufügen", + addSubelement:"Unterelement hinzufügen", + insert:"Einfügen", + xml:"Design", + text:"Quelle", + editing:"Bearbeite ", + noBrowserExportSupport:"Ihr Webbrowser unterstützt das übernehmen der Inhalte nicht. Um die Inhalte zu speichern wechseln sie bitte in the Textansicht und markieren und kopieren sie den Inhalt.", + filename:"Dateiname", + saveFailedXmlInvalid:"XML ist ungültig. Daten können nicht verarbeitet werden.", + saveFailedSeeErrors:"Die Übernahme der Änderungen ist fehlgeschlagen
        Siehe Fehlermeldung.", + export:"Export", + download:"Download", + submitting:"Verarbeite...", + failedToSubmit:"Das XML Dokument konnte nicht verabeitet werden", + documentUnchanged:"Keine Änderungen", + submitChanges:"Übernehmen", + noElements:"Das XML Dokument ist leer. Verwenden sie das Menü rechts um ein neuen Hauptelement einzufügen.", + unsavedChanges:"Nicht übernommene Änderungen", + savedChanges:"Änderungen gespeichert", + couldNotAdd1:"Das Kind-Element ", + couldNotAdd2:" kann nicht hinzugefügt werden, da es kein gültiges Kind des folgenden Tags is: ", + couldNotAddAttr1:"Das Attribut ", + couldNotAddAttr2:"kann nicht hinzugefügt werden, da es kein gültiges Attribut des folgenden Tags ist: ", + noDocument:"Das angegebene Dokument konnte nicht geladen werden und es ist kein Rückfalldokument definiert. Der Editor kann nicht starten.", + noStartingDocument:"Es ist kein Startdokument definiert.", + failedToAddChild:"Das Element des folgenden Typs konnte nicht hinzugefügt werden: ", + undo:"Strg+z", + redo:"Strg+y", + del:"Entf", + enter:"Enter", + elementUp:"Alt+oben", + elementDown:"Alt+unten", + esc:"Esc", + down:"unten", + up:"oben", + left:"links", + right:"rechts", + shiftDown:"Umschalt+unten", + shiftUp:"Umschalt+oben", + shiftLeft:"Umschalt+links", + shiftRight:"Umschalt+rechts", + altA:"alt+a", + altShiftE:"Alt+Umschalt+e", + altShiftX:"Alt+Umschalt+x", + altShiftS:"Alt+Umschalt+s", + altShiftT:"Alt+Umschalt+t", + file:"Datei", + edit:"Bearbeiten", + select:"Auswählen", + view:"Ansicht", + options:"Optionen", + deselect:"Abwählen", + nextElement:"Nächstes Element", + previousElement:"Vorheriges Element", + parentElement:"Eine Ebene höher", + firstChild:"Erstes Unterelement", + nextSibling:"Nächstes Paar", + previousSibling:"Vorheriges Paar", + nextAttribute:"Nächstes Attribut", + previousAttribute:"Vorheriges Attribut", + deleteElement:"Löschen", + moveElementUp:"nach oben", + moveElementDown:"nach unten", + undoMenuitem:"Rückgängig", + redoMenuitem:"Wiederherstellen", + switchToXml:"Grafische XML Anzeige", + switchToText:"XML-Quelle", + prettifyXml:"Automatische XML Formatierung", + enableShortcuts:"Direktzugriffstasten aktiv", + enforceMinMaxOccurs:"Min/Max Vorgaben erzwingen", + prependNewElements:"Keine neuen Elemente zulassen", + noConnection:"Kann keine Verbindung aufbauen.\nBitte Netzwerkverbindung prüfen.", + pageNotFound:"Die angeforderte Seite wurde nicht gefunden. [404]", + internalServerError:"Interner Server Fehler [500].", + jsonParseFailed:"Parsen des angeforderten JSON ist fehlgeschlagen.", + timeout:"Zeitüberschreitung der Serververbindung.", + ajaxAborted:"AJAX Anfrage abgebrochen.", + uncaughtError:"Unerwarteter Fehler.\n", + xmlSerializerNotSupported:"XML Serialisierung nicht unterstützt.", + alreadyExistent:"Existiert bereits", + useTheMenuToAddSubElementsAndAttr:"Weitere Elemente und Attribute über das Menü rechts hinzufügen.", + useTheMenuToAddAttr:"Weitere Attribute über das Menü hinzufügen.", + useTheMenuToAddSubElements:"Weitere Elemente über das Menü rechts hinzufügen.", + cannotSubmitNoPostOption:"Datei kann nicht übertragen werden - keine HTTP POST Optionen definiert.", + useMenuToAdd:"Unterelemente, Attribute und Text können über das Menü hinzugefügt werden.", + useMenuToAddAttributesText:"Attribute und Text können über das Menü hinzugefügt werden.", + useMenuToAddSubelementsText:"Unterelemente und Text können über das Menü hinzugefügt werden.", + useMenuToAddText:"Text kann über das Menü hinzugefügt werden.", + useMenuToAddSubelementsAttributes:"Unterelemente und Attribute können über das Menü hinzugefügt werden.", + useMenuToAddAttributes:"Attribute können über das Menü hinzugefügt werden.", + useMenuToAddSubelements:"Unterelemente können über das Menü hinzugefügt werden.", + unableToAddFixSyntax:"Element kann nicht hinzugefügt werden. Bitte zuerst die XML Syntax korrigieren.", + addCData:"CDATA hinzufügen", + addComment:"Kommentar hinzufügen", + addText:"Text hinzufügen", + add:"Hinzufügen von ", + xmlDocErrorInLine:"Fehler in der Zeile ", + xmlDocErrorAtPos:" an Position ", + xmlDocErrorCode:"Fehlercode: ", + xmlDocErrorReason:"Fehlergrund: ", + xmlDocErrorLine:"Fehlerzeile: ", + addChildElement:"Kind-Element hinzufügen", + addSiblingElement:"Schwesterelement hinzufügen", + addElementToParent:"Element zum übergeordneten Element hinzufügen", + addElementToRoot:"Element zum Wurzelelement hinzufügen", + addTextToElement:"Text zum Element hinzufügen", + addCommentToElement:"Kommentar zum Element hinzufügen", + addCDataToElement:"CDATA zum Element hinzufügen", + altE:"Alt+2", + altS:"Alt+s", + altP:"Alt+p", + altR:"Alt+r", + altT:"Alt+t", + altSlash:"Alt+/", + altComma:"Alt+,", + cancel:"Abbrechen", + choose:"Auswählen", + unableToLoadTemplate:"Die gewählte Vorlage konnte nicht geladen werden: ", + useMenuToAddContents:"Inhalte können über das Menü hinzugefügt werden." + } + }, + + userLang: getParam("lang", "en") + }, _create: function() { @@ -240,19 +528,19 @@ $.widget( "xml.xmlEditor", { if (this.options.submitErrorHandler == null) { this.options.submitErrorHandler = function(jqXHR, exception) { if (jqXHR.status === 0) { - alert('Not connect.\n Verify Network.'); + alert(this.options.i18n[this.options.userLang].noConnection); } else if (jqXHR.status == 404) { - alert('Requested page not found. [404]'); + alert(this.options.i18n[this.options.userLang].pageNotFound); } else if (jqXHR.status == 500) { - alert('Internal Server Error [500].'); + alert(this.options.i18n[this.options.userLang].internalServerError); } else if (exception === 'parsererror') { - alert('Requested JSON parse failed.'); + alert(this.options.i18n[this.options.userLang].jsonParseFailed); } else if (exception === 'timeout') { - alert('Time out error.'); + alert(this.options.i18n[this.options.userLang].timeout); } else if (exception === 'abort') { - alert('Ajax request aborted.'); + alert(this.options.i18n[this.options.userLang].ajaxAborted); } else { - alert('Uncaught Error.\n' + jqXHR.responseText); + alert(this.options.i18n[this.options.userLang].uncaughtError + jqXHR.responseText); } }; } @@ -312,7 +600,7 @@ $.widget( "xml.xmlEditor", { if (this.options.confirmExitWhenUnsubmitted) { $(window).bind('beforeunload', function(e) { if (self.xmlState != null && self.xmlState.isChanged()) { - return "The document contains unsaved changes."; + return this.options.i18n[this.options.userLang].unsavedChanges; } }); } @@ -322,6 +610,8 @@ $.widget( "xml.xmlEditor", { // Start loading the document for editing this.loadDocument(this.options.ajaxOptions, localXMLContent); + var editor = this.activeEditor; + setTimeout(function () { if (!!editor['selectNext']) { editor.selectNext(); } }, 200); }, // Load the schema object @@ -404,7 +694,7 @@ $.widget( "xml.xmlEditor", { // Document path didn't retrieve anything self._templating(); } else { - console.error("Could not specified document and no fallback provided, cannot start."); + console.error(self.options.i18n[self.options.userLang].noDocument); } } }); @@ -415,7 +705,7 @@ $.widget( "xml.xmlEditor", { // Fall back to templating if it was specified this._templating(); } else { - console.error("No starting document"); + console.error(self.options.i18n[self.options.userLang].noStartingDocument); } }, @@ -493,6 +783,8 @@ $.widget( "xml.xmlEditor", { this.constructEditor(); this.refreshDisplay(); this.activeEditor.selectRoot(); + this.modeChange(this.options.initialEditMode); // Optional initial source view change + this.refreshDisplay(); // Capture baseline undo state this.undoHistory.captureSnapshot(); }, @@ -506,7 +798,7 @@ $.widget( "xml.xmlEditor", { var editorHeaderBacking = $("
        ").addClass(editorHeaderClass + "_backing").appendTo(this.xmlWorkAreaContainer); this.editorHeader = $("
        ").attr('class', editorHeaderClass).appendTo(this.xmlWorkAreaContainer); if (this.options.documentTitle != null) - $("

        ").html("Editing Description: " + this.options.documentTitle).appendTo(this.editorHeader); + $("

        ").html(this.options.i18n[this.options.userLang].editing + this.options.documentTitle).appendTo(this.editorHeader); this.menuBar.render(this.editorHeader); editorHeaderBacking.height(this.editorHeader.outerHeight()); // Create grouping of header elements that need to be positioned together @@ -522,12 +814,12 @@ $.widget( "xml.xmlEditor", { $(window).resize($.proxy(this.resize, this)); this.modifyMenu.initialize(this.xmlEditorContainer); - this.modifyMenu.addMenu(addElementMenuClass, this.options.addElementMenuHeaderText, + this.modifyMenu.addMenu(addElementMenuClass, this.options.i18n[this.options.userLang].addSubelement, true, false, true); - this.modifyMenu.addAttributeMenu(addAttrMenuClass, this.options.addAttrMenuHeaderText, + this.modifyMenu.addAttributeMenu(addAttrMenuClass, this.options.i18n[this.options.userLang].addAttribute, true, false, true); - this.modifyMenu.addNodeMenu(addNodeMenuClass, "Add Nodes", true, false); - this.addTopLevelMenu = this.modifyMenu.addMenu(addTopMenuClass, this.options.addTopMenuHeaderText, + this.modifyMenu.addNodeMenu(addNodeMenuClass, this.options.i18n[this.options.userLang].addNodes, true, false); + this.addTopLevelMenu = this.modifyMenu.addMenu(addTopMenuClass, this.options.i18n[this.options.userLang].addRoot, true, true, false, function(target) { var selectedElement = self.guiEditor.selectedElement; if (!selectedElement || selectedElement.length == 0 || selectedElement.isRootElement) @@ -553,7 +845,7 @@ $.widget( "xml.xmlEditor", { // Resize event for refreshing menu and editor sizes resize: function () { - this.xmlTabContainer.width(this.xmlEditorContainer.outerWidth() - this.modifyMenu.menuColumn.outerWidth()); + this.xmlTabContainer.width(this.xmlEditorContainer.outerWidth() - (this.modifyMenu.menuColumn != null ? this.modifyMenu.menuColumn.outerWidth() : 0)); if (this.activeEditor != null){ this.activeEditor.resize(); } @@ -561,6 +853,9 @@ $.widget( "xml.xmlEditor", { if (this.options.floatingMenu) { this.modifyMenu.setMenuPosition(); } + if (!this.options.enableEdit && this.modifyMenu.menuColumn != null) { // no edit enabled => don't show modify menu + this.modifyMenu.menuColumn.style.width = "0px"; + } }, // Event which triggers the creation of a new child element, as defined by an instigator such as a menu @@ -583,7 +878,7 @@ $.widget( "xml.xmlEditor", { try { this.setXMLFromEditor(); } catch (e) { - this.addProblem("Unable to add element, please fix existing XML syntax first.", e); + this.addProblem(this.options.i18n[this.options.userLang].unableToAddFixSyntax, e); return; } } @@ -628,7 +923,7 @@ $.widget( "xml.xmlEditor", { } if (newElement == null) { - return "Failed to add child of type " + newElementDefinition; + return this.options.i18n[this.options.userLang].failedToAddChild + newElementDefinition; } // Trigger post element creation event in the currently active editor to handle UI updates @@ -676,7 +971,7 @@ $.widget( "xml.xmlEditor", { } if (!objectType && !xmlElement.objectType.anyAttribute) { - return "Could not add attribute " + attrDefinition + ", it is not a valid for element " + xmlElement.objectType.localName; + return this.options.i18n[this.options.userLang].couldNotAddAttr1 + attrDefinition + this.options.i18n[this.options.userLang].couldNotAddAttr2 + xmlElement.objectType.localName; } } @@ -778,10 +1073,12 @@ $.widget( "xml.xmlEditor", { } if (mode == 0) { this.activeEditor = this.guiEditor; - $("#" + xmlMenuHeaderPrefix + this.options.xmlEditorLabel.replace(/ /g, "_")).addClass("active_mode_tab"); + var editor = this.activeEditor; + setTimeout(function() {editor.selectNext();}, 200); + $("#" + xmlMenuHeaderPrefix + this.options.i18n[this.options.userLang].xml.replace(/ /g, "_")).addClass("active_mode_tab"); } else { this.activeEditor = this.textEditor; - $("#" + xmlMenuHeaderPrefix + this.options.textEditorLabel.replace(/ /g, "_")).addClass("active_mode_tab"); + $("#" + xmlMenuHeaderPrefix + this.options.i18n[this.options.userLang].text.replace(/ /g, "_")).addClass("active_mode_tab"); } this.activeEditor.activate(); if (this.ready) @@ -797,7 +1094,13 @@ $.widget( "xml.xmlEditor", { if (this.options.floatingMenu) { this.modifyMenu.setMenuPosition(); } - this.xmlWorkAreaContainer.width(this.xmlEditorContainer.outerWidth() - this.modifyMenu.menuColumn.outerWidth()); + try { + if (this.modifyMenu.menuColumn != null) { + this.xmlWorkAreaContainer.width(this.xmlEditorContainer.outerWidth() - this.modifyMenu.menuColumn.outerWidth()); + } + } catch(e) { + console.log(e); + } }, setTextArea : function(xmlString) { @@ -815,6 +1118,25 @@ $.widget( "xml.xmlEditor", { } }, + getXMLString: function() { + if (this.textEditor.active) { + var xmlString = this.textEditor.aceEditor.getValue(); + try { + this.xmlState.setXMLFromString(xmlString); + } catch (e) { + // Ignore error, continue to return last GUI value instead + } + } + return this.xml2Str(this.xmlState.xml); + }, + + getText: function() { + if (this.textEditor.active) { + return this.textEditor.aceEditor.getValue(); + } + return this.xml2Str(this.xmlState.xml); + }, + // Callback for submit button pressing. Performs a submit function and then uploads the // document to the provided URL, if configured to do either submitXML: function(config) { @@ -830,12 +1152,12 @@ $.widget( "xml.xmlEditor", { // Export the contents of the editor as text to a file, as supported by browsers exportXML: function() { if (typeof(Blob) === "undefined") { - this.addProblem("Browser does not support saving files via this editor. To save, copy and paste the document from the Text view."); + this.addProblem(this.options.i18n[this.options.userLang].noBrowserExportSupport); return false; } - var exportDialog = $("
        ") - .dialog({modal: true, dialogClass: 'xml_dialog', resizable : false, title: 'Enter file name', height: 80}); + var exportDialog = $("
        ") + .dialog({modal: true, dialogClass: 'xml_dialog', resizable : false, title: this.options.i18n[this.options.userLang].filename, height: 80}); var self = this; exportDialog.submit(function(){ if (self.textEditor.active) { @@ -843,8 +1165,8 @@ $.widget( "xml.xmlEditor", { self.setXMLFromEditor(); } catch (e) { self.xmlState.setDocumentHasChanged(true); - $("." + submissionStatusClass).html("Failed to save
        See errors at top").css("background-color", "#ffbbbb").animate({backgroundColor: "#ffffff"}, 1000); - self.addProblem("Cannot save due to invalid xml", e); + $("." + submissionStatusClass).html(this.options.i18n[this.options.userLang].saveFailedSeeErrors).css("background-color", "#ffbbbb").animate({backgroundColor: "#ffffff"}, 1000); + self.addProblem(this.options.i18n[this.options.userLang].saveFailedXmlInvalid, e); return false; } } @@ -856,7 +1178,7 @@ $.widget( "xml.xmlEditor", { var fileName = exportDialog.find('input[type="text"]').val(); if (!fileName) fileName = "file.xml"; - var download = $('Download ' + fileName + '').attr("href", url); + var download = $('' + this.options.i18n[this.options.userLang].download + ' ' + fileName + '').attr("href", url); download.attr("download", fileName); exportDialog.empty().append(download); return false; @@ -869,24 +1191,25 @@ $.widget( "xml.xmlEditor", { if (this.submitButtonConfigs.length > 0 && this.submitButtonConfigs[0].url) { config = this.submitButtonConfigs[0]; } else { - this.addProblem("Cannot submit because no post Options"); + this.addProblem(this.options.i18n[this.options.userLang].cannotSubmitNoPostOption); return; } } + $(':focus').blur(); // Ensure focus is removed before saving, so that finished content saved if (this.textEditor.active) { try { this.setXMLFromEditor(); } catch (e) { this.xmlState.setDocumentHasChanged(true); - $("." + submissionStatusClass).html("Failed to submit
        See errors at top").css("background-color", "#ffbbbb").animate({backgroundColor: "#ffffff"}, 1000); - this.addProblem("Cannot submit due to invalid xml", e); + $("." + submissionStatusClass).html(this.options.i18n[this.options.userLang].saveFailedSeeErrors).css("background-color", "#ffbbbb").animate({backgroundColor: "#ffffff"}, 1000); + this.addProblem(this.options.i18n[this.options.userLang].saveFailedXmlInvalid, e); return false; } } // convert XML DOM to string var xmlString = this.xml2Str(this.xmlState.xml); - $("." + submissionStatusClass).html("Submitting..."); + $("." + submissionStatusClass).html(this.options.i18n[this.options.userLang].submitting); var self = this; $.ajax({ url : config.url, @@ -903,8 +1226,8 @@ $.widget( "xml.xmlEditor", { self.clearProblemPanel(); } else { self.xmlState.syncedChangeEvent(); - $("." + submissionStatusClass).html("Failed to submit
        See errors at top").css("background-color", "#ffbbbb").animate({backgroundColor: "#ffffff"}, 1000); - self.addProblem("Failed to submit xml document", outcome); + $("." + submissionStatusClass).html(this.options.i18n[this.options.userLang].saveFailedSeeErrors).css("background-color", "#ffbbbb").animate({backgroundColor: "#ffffff"}, 1000); + self.addProblem(this.options.i18n[this.options.userLang].failedToSubmit, outcome); } }, error : function(jqXHR, exception) { @@ -944,7 +1267,7 @@ $.widget( "xml.xmlEditor", { // Internet Explorer. return xmlNode.xml; } catch (e) { - this.addProblem('Xmlserializer not supported', e); + this.addProblem(this.options.i18n[this.options.userLang].xmlSerializerNotSupported, e); return false; } } @@ -995,11 +1318,15 @@ $.widget( "xml.xmlEditor", { setEnableKeybindings : function(enable) { if (enable) { this.options.enableGUIKeybindings = true; - this.menuBar.menuBarContainer.removeClass("xml_bindings_disabled"); + if (this.menuBar.menuBarContainer) { + this.menuBar.menuBarContainer.removeClass("xml_bindings_disabled"); + } $(window).on("keydown.xml_keybindings", $.proxy(this.keydownCallback, this)); } else { this.options.enableGUIKeybindings = false; - this.menuBar.menuBarContainer.addClass("xml_bindings_disabled"); + if (this.menuBar.menuBarContainer) { + this.menuBar.menuBarContainer.addClass("xml_bindings_disabled"); + } $(window).off("keydown.xml_keybindings"); } }, @@ -1177,22 +1504,31 @@ $.widget( "xml.xmlEditor", { // Menu Update functions refreshMenuUndo: function(self) { if (self.undoHistory.headIndex > 0) { - $("#" + xmlMenuHeaderPrefix + "Undo").removeClass("disabled").data("menuItemData").enabled = true; + $("#" + xmlMenuHeaderPrefix + self.options.i18n[self.options.userLang].undoMenuitem.replaceAll(' ','_')).removeClass("disabled").data("menuItemData").enabled = true; } else { - $("#" + xmlMenuHeaderPrefix + "Undo").addClass("disabled").data("menuItemData").enabled = false; + $("#" + xmlMenuHeaderPrefix + self.options.i18n[self.options.userLang].undoMenuitem.replaceAll(' ','_')).addClass("disabled").data("menuItemData").enabled = false; } if (self.undoHistory.headIndex < self.undoHistory.states.length - 1) { - $("#" + xmlMenuHeaderPrefix + "Redo").removeClass("disabled").data("menuItemData").enabled = true; + $("#" + xmlMenuHeaderPrefix + self.options.i18n[self.options.userLang].redoMenuitem.replaceAll(' ','_')).removeClass("disabled").data("menuItemData").enabled = true; } else { - $("#" + xmlMenuHeaderPrefix + "Redo").addClass("disabled").data("menuItemData").enabled = false; + $("#" + xmlMenuHeaderPrefix + self.options.i18n[self.options.userLang].redoMenuitem.replaceAll(' ','_')).addClass("disabled").data("menuItemData").enabled = false; } }, // Performs updates to the menu for changing element/attribute selection refreshMenuSelected: function(self) { - var suffixes = ['Deselect', 'Next_Element', 'Previous_Element', 'Parent', 'First_Child', 'Next_Sibling', - 'Previous_Sibling', 'Next_Attribute', 'Previous_Attribute', 'Delete', 'Move_Element_Up', - 'Move_Element_Down']; + var suffixes = [self.options.i18n[self.options.userLang].deselect.replaceAll(' ','_'), + self.options.i18n[self.options.userLang].nextElement.replaceAll(' ','_'), + self.options.i18n[self.options.userLang].previousElement.replaceAll(' ','_'), + self.options.i18n[self.options.userLang].parentElement.replaceAll(' ','_'), + self.options.i18n[self.options.userLang].firstChild.replaceAll(' ','_'), + self.options.i18n[self.options.userLang].nextSibling.replaceAll(' ','_'), + self.options.i18n[self.options.userLang].previousSibling.replaceAll(' ','_'), + self.options.i18n[self.options.userLang].nextAttribute.replaceAll(' ','_'), + self.options.i18n[self.options.userLang].previousAttribute.replaceAll(' ','_'), + self.options.i18n[self.options.userLang].deleteElement.replaceAll(' ','_'), + self.options.i18n[self.options.userLang].moveElementUp.replaceAll(' ','_'), + self.options.i18n[self.options.userLang].moveElementDown.replaceAll(' ','_')]; var hasSelected = self.guiEditor.selectedElement != null && self.guiEditor.active; $.each(suffixes, function(){ if (hasSelected) diff --git a/src/menu_bar.js b/src/menu_bar.js index a3c4fb0..bea1ec9 100644 --- a/src/menu_bar.js +++ b/src/menu_bar.js @@ -3,6 +3,7 @@ * menus or options may be added as well. Supports refreshing of menu items states via externally * defined updateFunctions */ + function MenuBar(editor) { this.editor = editor; this.menuBarContainer = null; @@ -21,147 +22,169 @@ function MenuBar(editor) { // Default menu entries this.headerMenuData = [ { - label : 'File', + label : self.editor.options.i18n[self.editor.options.userLang].file, enabled : true, + show: true, action : function(event) {self.activateMenu(event);}, items : [ { - label : 'Submit to Server', + label : self.editor.options.i18n[self.editor.options.userLang].submitChanges, enabled : defaultSubmitConfig != null, - binding : "ctrl+alt+s", + show: self.editor.options.enableEdit, + binding : self.editor.options.i18n[self.editor.options.userLang].altShiftS, action : function() { self.editor.uploadXML.call(self.editor, defaultSubmitConfig); } }, { - label : 'Export', + label : self.editor.options.i18n[self.editor.options.userLang].export, enabled : (typeof(Blob) !== undefined), - binding : "ctrl+alt+e", + show: true, + binding : self.editor.options.i18n[self.editor.options.userLang].altShiftE, action : $.proxy(self.editor.exportXML, self.editor) } ] }, { - label : 'Edit', - enabled : true, + label : self.editor.options.i18n[self.editor.options.userLang].edit, + enabled : self.editor.options.enableEdit, // readonly mode + show: self.editor.options.enableEdit, // readonly mode action : function(event) {self.activateMenu(event);}, items : [ { - label : 'Undo', - enabled : false, - binding : "ctrl+z or mac+z", + label : self.editor.options.i18n[self.editor.options.userLang].undoMenuitem, + enabled : true, + show: true, + binding : self.editor.options.i18n[self.editor.options.userLang].undo, action : function() { self.editor.undoHistory.changeHead(-1); } }, { - label : 'Redo', - enabled : false, - binding : "ctrl+y or mac+shift+z", + label : self.editor.options.i18n[self.editor.options.userLang].redoMenuitem, + enabled : true, + show: true, + binding : self.editor.options.i18n[self.editor.options.userLang].redo, action : function() { self.editor.undoHistory.changeHead(1); } }, { - label : 'Delete', + label : self.editor.options.i18n[self.editor.options.userLang].deleteElement, enabled : true, - binding : "del", + show: true, + binding : self.editor.options.i18n[self.editor.options.userLang].del, action : function(){ self.editor.guiEditor.deleteSelected(); } }, { - label : 'Move Element Up', + label : self.editor.options.i18n[self.editor.options.userLang].moveElementUp, enabled : true, - binding : "alt+up", + show: true, + binding : self.editor.options.i18n[self.editor.options.userLang].elementUp, action : function(){ self.editor.guiEditor.moveSelected(true); } }, { - label : 'Move Element Down', + label : self.editor.options.i18n[self.editor.options.userLang].moveElementDown, enabled : true, - binding : "alt+down", + show: true, + binding : self.editor.options.i18n[self.editor.options.userLang].elementDown, action : function(){ self.editor.guiEditor.moveSelected(); } } ] }, { - label : 'Select', - enabled : true, + label : self.editor.options.i18n[self.editor.options.userLang].select, + enabled : self.editor.options.enableEdit, // readonly mode + show: self.editor.options.enableEdit, // readonly mode action : function(event) {self.activateMenu(event);}, items : [ { - label : 'Deselect', + label : self.editor.options.i18n[self.editor.options.userLang].deselect, enabled : true, - binding : "esc", + show: true, + binding : self.editor.options.i18n[self.editor.options.userLang].esc, action : function(){ self.editor.guiEditor.deselect(); } },{ - label : 'Next Element', + label : self.editor.options.i18n[self.editor.options.userLang].nextElement, enabled : true, - binding : "down", + show: true, + binding : self.editor.options.i18n[self.editor.options.userLang].down, action : function(){ self.editor.guiEditor.selectNext(); } }, { - label : 'Previous Element', + label : self.editor.options.i18n[self.editor.options.userLang].previousElement, enabled : true, - binding : "up", + show: true, + binding : self.editor.options.i18n[self.editor.options.userLang].up, action : function(){ self.editor.guiEditor.selectNext(true); } }, { - label : 'Next Attribute', + label : self.editor.options.i18n[self.editor.options.userLang].nextAttribute, enabled : true, - binding : "right", + show: true, + binding : self.editor.options.i18n[self.editor.options.userLang].right, action : function(){ self.editor.guiEditor.selectAttribute(); } }, { - label : 'Previous Attribute', + label : self.editor.options.i18n[self.editor.options.userLang].previousAttribute, enabled : true, - binding : "left", + show: true, + binding : self.editor.options.i18n[self.editor.options.userLang].left, action : function(){ self.editor.guiEditor.selectAttribute(true); } }, { - label : 'Parent', + label : self.editor.options.i18n[self.editor.options.userLang].parentElement, enabled : true, - binding : "shift+left", + show: true, + binding : self.editor.options.i18n[self.editor.options.userLang].shiftLeft, action : function(){ self.editor.guiEditor.selectParent(); } }, { - label : 'First Child', + label : self.editor.options.i18n[self.editor.options.userLang].firstChild, enabled : true, - binding : "shift+right", + show: true, + binding : self.editor.options.i18n[self.editor.options.userLang].shiftRight, action : function(){ self.editor.guiEditor.selectParent(true); } }, { - label : 'Next Sibling', + label : self.editor.options.i18n[self.editor.options.userLang].nextSibling, enabled : true, - binding : "shift+down", + show: true, + binding : self.editor.options.i18n[self.editor.options.userLang].shiftDown, action : function(){ self.editor.guiEditor.selectSibling(); } }, { - label : 'Previous Sibling', + label : self.editor.options.i18n[self.editor.options.userLang].previousSibling, enabled : true, - binding : "shift+up", + show: true, + binding : self.editor.options.i18n[self.editor.options.userLang].shiftUp, action : function(){ self.editor.guiEditor.selectSibling(true); } } ] }, { - label : 'Insert', + label : self.editor.options.i18n[self.editor.options.userLang].insert, enabled : true, + show: self.editor.options.enableEdit, action : function(event) {self.activateMenu(event);}, items : [ { - label : 'Add attribute', + label : self.editor.options.i18n[self.editor.options.userLang].addAttribute, enabled : true, - binding : "alt+a", + show: true, + binding : self.editor.options.i18n[self.editor.options.userLang].altA, action : function(){ var selected = self.editor.guiEditor.selectedElement; if (selected instanceof XMLElement) self.editor.addNode(selected, "attribute", false); } }, { - label : 'Add element', + label : self.editor.options.i18n[self.editor.options.userLang].addElement, enabled : true, - binding : "enter", + show: true, + binding : self.editor.options.i18n[self.editor.options.userLang].enter, action : function(){ var selected = self.editor.guiEditor.selectedElement; if (selected instanceof XMLElement) { @@ -169,61 +192,68 @@ function MenuBar(editor) { } } }, { - label : 'Add child element', + label : self.editor.options.i18n[self.editor.options.userLang].addChildElement, enabled : true, - binding : "alt+e", + show: true, + binding : self.editor.options.i18n[self.editor.options.userLang].addE, action : function(){ var selected = self.editor.guiEditor.selectedElement; if (selected instanceof XMLElement) self.editor.addNode(selected, "element", false); } }, { - label : 'Add sibling element', + label : self.editor.options.i18n[self.editor.options.userLang].addSiblingElement, enabled : true, - binding : "alt+s", + show: true, + binding : self.editor.options.i18n[self.editor.options.userLang].addS, action : function(){ var selected = self.editor.guiEditor.selectedElement; if (selected) self.editor.addNode(selected.parentElement, "element", false, selected); } }, { - label : 'Add element to parent', + label : self.editor.options.i18n[self.editor.options.userLang].addElementToParent, enabled : true, - binding : "alt+p", + show: true, + binding : self.editor.options.i18n[self.editor.options.userLang].addP, action : function(){ var selected = self.editor.guiEditor.selectedElement; if (selected) self.editor.addNode(selected.parentElement, "element", false); } }, { - label : 'Add element to root', + label : self.editor.options.i18n[self.editor.options.userLang].addElementToRoot, enabled : true, - binding : "alt+r", + show: true, + binding : self.editor.options.i18n[self.editor.options.userLang].altR, action : function(){ self.editor.addNode(self.editor.guiEditor.rootElement, "element", false); } }, { - label : 'Add text to element', + label : self.editor.options.i18n[self.editor.options.userLang].addTextToElement, enabled : true, - binding : "alt+t", + show: true, + binding : self.editor.options.i18n[self.editor.options.userLang].altT, action : function(){ var selected = self.editor.guiEditor.selectedElement; if (selected) self.editor.addNode(selected, "text", false); } }, { - label : 'Add comment to element', + label : self.editor.options.i18n[self.editor.options.userLang].addCommentToElement, enabled : true, - binding : "alt+/", + show: true, + binding : self.editor.options.i18n[self.editor.options.userLang].altSlash, action : function(){ var selected = self.editor.guiEditor.selectedElement; if (selected) self.editor.addNode(selected, "comment", false); } }, { - label : 'Add CDATA to element', + label : self.editor.options.i18n[self.editor.options.userLang].addCDataToElement, enabled : true, - binding : "alt+,", + show: true, + binding : self.editor.options.i18n[self.editor.options.userLang].altComma, action : function(){ var selected = self.editor.guiEditor.selectedElement; if (selected) @@ -231,47 +261,52 @@ function MenuBar(editor) { } } ] }, { - label : 'View', + label : self.editor.options.i18n[self.editor.options.userLang].view, enabled : true, + show: true, action : function(event) {self.activateMenu(event);}, items : [ { - label : 'Switch to XML View', + label : self.editor.options.i18n[self.editor.options.userLang].switchToXml, enabled : true, - binding : "ctrl+alt+1", + binding : self.editor.options.i18n[self.editor.options.userLang].altShiftX, action : function() { self.editor.modeChange(0); } }, { - label : 'Switch to Text View', + label : self.editor.options.i18n[self.editor.options.userLang].switchToText, enabled : true, - binding : "ctrl+alt+2", + binding : self.editor.options.i18n[self.editor.options.userLang].altShiftT, action : function() { self.editor.modeChange(1); } } ] }, { - label : 'Options', + label : self.editor.options.i18n[self.editor.options.userLang].options, enabled : true, + show: true, action : function(event) {self.activateMenu(event);}, items : [ { - label : 'Pretty XML Formatting', + label : self.editor.options.i18n[self.editor.options.userLang].prettifyXml, enabled : true, + show: true, checked : self.editor.options.prettyXML, action : function() { self.editor.options.prettyXML = !self.editor.options.prettyXML; self.checkEntry(this, self.editor.options.prettyXML); } }, { - label : 'Enable shortcut keys', + label : self.editor.options.i18n[self.editor.options.userLang].enableShortcuts, enabled : true, + show: true, checked : self.editor.options.enableGUIKeybindings, action : function() { self.editor.setEnableKeybindings(!self.editor.options.enableGUIKeybindings); self.checkEntry(this, self.editor.options.enableGUIKeybindings); } }, { - label : 'Enforce min/max occurs', + label : self.editor.options.i18n[self.editor.options.userLang].enforceMinMaxOccurs, enabled : self.editor.options.enforceOccurs, + show: true, checked : self.editor.options.enforceOccurs, action : function() { self.editor.options.enforceOccurs = !self.editor.options.enforceOccurs; @@ -279,8 +314,9 @@ function MenuBar(editor) { self.checkEntry(this, self.editor.options.enforceOccurs); } }, { - label : 'Prepend new elements', + label : self.editor.options.i18n[self.editor.options.userLang].prependNewElements, enabled : true, + show: true, checked : self.editor.options.prependNewElements, action : function() { self.editor.options.prependNewElements = !self.editor.options.prependNewElements; @@ -298,15 +334,17 @@ function MenuBar(editor) { action : "http://www.loc.gov/standards/mods/mods-outline.html" } ] }*/, { - label : self.editor.options.xmlEditorLabel, + label : self.editor.options.i18n[self.editor.options.userLang].xml, enabled : true, + show: true, itemClass : 'header_mode_tab', action : function() { self.editor.modeChange(0); } }, { - label : self.editor.options.textEditorLabel, + label : self.editor.options.i18n[self.editor.options.userLang].text, enabled : true, + show: true, itemClass : 'header_mode_tab', action : function() { self.editor.modeChange(1); @@ -334,16 +372,20 @@ MenuBar.prototype.activateMenu = function(event) { // Builds the menu and attaches it to the editor MenuBar.prototype.render = function(parentElement) { this.parentElement = parentElement; - this.menuBarContainer = $("
        ").addClass(xmlMenuBarClass).appendTo(parentElement); + if (this.editor.options.sourceDesignSwitch) {// Enable hiding the XML/Source switch buttons + this.menuBarContainer = $("
        ").addClass(xmlMenuBarClass).appendTo(parentElement); - this.headerMenu = $("
          "); - this.menuBarContainer.append(this.headerMenu); - this.initEventHandlers(); + this.headerMenu = $("
            "); + this.menuBarContainer.append(this.headerMenu); + this.initEventHandlers(); - var menuBar = this; - $.each(this.headerMenuData, function() { - menuBar.generateMenuItem(this, menuBar.headerMenu); - }); + var menuBar = this; + $.each(this.headerMenuData, function() { + if (this.show) { // Enable hiding unwanted menus + menuBar.generateMenuItem(this, menuBar.headerMenu); + } + }); + } }; MenuBar.prototype.initEventHandlers = function() { @@ -419,4 +461,4 @@ MenuBar.prototype.checkEntry = function(menuItem, checked) { } else { menuItem.find(".xml_menu_check").html(""); } -}; \ No newline at end of file +}; diff --git a/src/modify_element_menu.js b/src/modify_element_menu.js index 6697763..43ec44c 100644 --- a/src/modify_element_menu.js +++ b/src/modify_element_menu.js @@ -1,6 +1,7 @@ /** * Menu object for adding new elements to an existing element or document */ + function ModifyElementMenu(menuID, label, expanded, enabled, owner, editor, getRelativeToFunction) { this.menuID = menuID; this.label = label; diff --git a/src/modify_menu_panel.js b/src/modify_menu_panel.js index cee89ff..609c7ea 100644 --- a/src/modify_menu_panel.js +++ b/src/modify_menu_panel.js @@ -1,6 +1,7 @@ /** * Menu panel for managing individual modification menus. */ + function ModifyMenuPanel(editor) { this.editor = editor; this.menus = {}; @@ -10,16 +11,17 @@ function ModifyMenuPanel(editor) { } ModifyMenuPanel.prototype.initialize = function (parentContainer) { - this.menuColumn = $("
            ").attr('class', menuColumnClass).appendTo(parentContainer); + if (this.editor.options.enableEdit) // Enable edit switch does control the visibility of some elements. + this.menuColumn = $("
            ").attr('class', menuColumnClass).appendTo(parentContainer); // Generate the document status panel, which shows a save/export button as well as if there are changes to the document if (this.editor.options.enableDocumentStatusPanel) { var self = this; var documentStatusPanel = $(self.editor.options.documentStatusPanelDomId); - $("").addClass(submissionStatusClass).html("Document is unchanged") + $("").addClass(submissionStatusClass).html(self.editor.options.i18n[self.editor.options.userLang].documentUnchanged) .appendTo(documentStatusPanel); - if (self.editor.submitButtonConfigs != null){ + if (self.editor.options.showExport && self.editor.submitButtonConfigs != null){ $.each(self.editor.submitButtonConfigs, function(index, config){ var submitButton; if (config.id && ('createDomElement' in config) && !config.createDomElement) { diff --git a/src/namespace_list.js b/src/namespace_list.js index e88dbc5..48e0856 100644 --- a/src/namespace_list.js +++ b/src/namespace_list.js @@ -33,4 +33,4 @@ NamespaceList.prototype.getNamespacePrefix = function(nsURI) { if (prefix) prefix += ":"; return prefix; -}; \ No newline at end of file +}; diff --git a/src/schema_tree.js b/src/schema_tree.js index 6658279..92e83c0 100644 --- a/src/schema_tree.js +++ b/src/schema_tree.js @@ -2,6 +2,7 @@ * Unpacks the elements of the schema object into structures to accomodate lookup * of definitions by name and position within the schema hierarchy. */ + function SchemaTree(rootElement) { // Map of elements stored by name. If there are name collisions, then elements are stored in a list this.nameToDef = {}; @@ -114,4 +115,4 @@ SchemaTree.prototype.pathMatches = function(elementNode, definition) { } } return false; -}; \ No newline at end of file +}; diff --git a/src/text_editor.js b/src/text_editor.js index 162c9cb..e261f27 100644 --- a/src/text_editor.js +++ b/src/text_editor.js @@ -1,6 +1,7 @@ /** * Editor object for doing text editing of the XML document using the cloud9 editor */ + function TextEditor(editor) { this.editor = editor; this.aceEditor = null; @@ -22,6 +23,7 @@ TextEditor.prototype.initialize = function(parentContainer) { this.xmlContent = $("
            ").attr({'id' : textContentClass + this.editor.instanceNumber, 'class' : textContentClass}).appendTo(parentContainer); this.xmlEditorDiv = $("
            ").attr('id', 'text_editor').appendTo(this.xmlContent); this.aceEditor = ace.edit("text_editor"); + this.aceEditor.setReadOnly(!this.editor.options.enableEdit); // readonly for the ace editor this.aceEditor.setTheme("ace/theme/textmate"); this.aceEditor.getSession().setMode("ace/mode/xml"); this.aceEditor.setShowPrintMargin(false); @@ -145,10 +147,14 @@ TextEditor.prototype.resize = function() { this.xmlContent.css({'height': xmlEditorHeight + 'px'}); this.xmlEditorDiv.width(this.xmlContent.innerWidth()); this.xmlEditorDiv.height(xmlEditorHeight); - if (this.editor.modifyMenu.menuContainer != null){ + if (this.editor.modifyMenu.menuContainer != null && this.editor.modifyMenu.menuContainer.offset() != null){ this.editor.modifyMenu.menuContainer.css({ 'max-height': $(this.editor.xmlWorkAreaContainer).height() - this.editor.modifyMenu.menuContainer.offset().top }); + } else { + this.editor.modifyMenu.menuContainer.css({ + 'max-height': $(this.editor.xmlWorkAreaContainer).height() - 10 + }); } if (this.aceEditor != null) this.aceEditor.resize(); diff --git a/src/undo_history.js b/src/undo_history.js index 304b484..ead4405 100644 --- a/src/undo_history.js +++ b/src/undo_history.js @@ -4,6 +4,7 @@ * Current implementation involves storing previous states of the XML document, * recorded each time a significant change occurs or the document is regenerated */ + function UndoHistory(xmlState, editor) { this.xmlState = xmlState; this.editor = editor; diff --git a/src/xml_attribute.js b/src/xml_attribute.js index 02f60a8..c7892d8 100644 --- a/src/xml_attribute.js +++ b/src/xml_attribute.js @@ -1,6 +1,7 @@ /** * Stores data representing a single attribute for an element */ + function XMLAttribute(objectType, xmlElement, editor) { AbstractXMLObject.call(this, objectType, editor); // the XMLElement object which this attribute belongs to. @@ -36,10 +37,11 @@ XMLAttribute.prototype.render = function (){ }).data('xmlAttribute', this).appendTo(this.xmlElement.getAttributeContainer()); var self = this; - var removeButton = document.createElement('a'); - removeButton.appendChild(document.createTextNode('(x) ')); - this.domNode[0].appendChild(removeButton); - + if ("required" != this.objectType.use && this.editor.options.enableEdit) { // Don't add required attributes in readonly mode + var removeButton = document.createElement('a'); + removeButton.appendChild(document.createTextNode('(x) ')); + this.domNode[0].appendChild(removeButton); + } var label = document.createElement('label'); var prefix = this.editor.xmlState.namespaces.getNamespacePrefix(this.objectType.namespace); label.appendChild(document.createTextNode(prefix + this.objectType.localName)); diff --git a/src/xml_autocomplete.js b/src/xml_autocomplete.js index a255607..f56fe8b 100644 --- a/src/xml_autocomplete.js +++ b/src/xml_autocomplete.js @@ -53,4 +53,4 @@ $.widget( "custom.xml_autocomplete", $.ui.autocomplete, { this._super(direction, event); this._resizeMenu(); } -}); \ No newline at end of file +}); diff --git a/src/xml_cdata_node.js b/src/xml_cdata_node.js index 4de05c2..1adc4d9 100644 --- a/src/xml_cdata_node.js +++ b/src/xml_cdata_node.js @@ -72,4 +72,4 @@ XMLCDataNode.prototype.focus = function() { XMLCDataNode.prototype.isSelected = function() { return AbstractXMLObject.prototype.isSelected.call(this); -}; \ No newline at end of file +}; diff --git a/src/xml_comment_node.js b/src/xml_comment_node.js index 41b1cf4..1b2201a 100644 --- a/src/xml_comment_node.js +++ b/src/xml_comment_node.js @@ -72,4 +72,4 @@ XMLCommentNode.prototype.focus = function() { XMLCommentNode.prototype.isSelected = function() { return AbstractXMLObject.prototype.isSelected.call(this); -}; \ No newline at end of file +}; diff --git a/src/xml_element.js b/src/xml_element.js index d97a9f1..15cec26 100644 --- a/src/xml_element.js +++ b/src/xml_element.js @@ -2,6 +2,7 @@ * Stores data related to a single xml element as it is represented in both the base XML * document and GUI */ + function XMLElement(xmlNode, objectType, editor) { AbstractXMLObject.call(this, objectType, editor); // jquery object reference to the xml node represented by this object in the active xml document @@ -223,6 +224,14 @@ XMLElement.prototype.populateChildren = function() { } } }); + // Patch to add "required" attributes. Does require a secondary change to know the "use" attribute also. See xsd2json-use.js : SchemaProcessor.prototype.createDefinition + $.each(this.objectType.attributes, function(){ + if ("required" == this.use) { + var childElement = self.addAttribute(this); + // Needs a patch in addAttributeEvent, too -> non-provided addButton parameter must be handled. + self.editor.activeEditor.addAttributeEvent(self, childElement); + } + }); }; XMLElement.prototype.initializeGUI = function () { @@ -251,24 +260,25 @@ XMLElement.prototype.addTopActions = function () { this.toggleCollapse.appendChild(document.createTextNode('_')); topActionSpan.appendChild(this.toggleCollapse); - var moveDown = document.createElement('span'); - moveDown.className = 'move_down'; - moveDown.id = this.domNodeID + '_down'; - moveDown.appendChild(document.createTextNode('\u2193')); - topActionSpan.appendChild(moveDown); - - var moveUp = document.createElement('span'); - moveUp.className = 'move_up'; - moveUp.id = this.domNodeID + '_up'; - moveUp.appendChild(document.createTextNode('\u2191')); - topActionSpan.appendChild(moveUp); - - var deleteButton = document.createElement('span'); - deleteButton.className = 'xml_delete'; - deleteButton.id = this.domNodeID + '_del'; - deleteButton.appendChild(document.createTextNode('X')); - topActionSpan.appendChild(deleteButton); - + if (this.editor.options.enableEdit) { // Only show menu entries in edit mode, not readonly mode + var moveDown = document.createElement('span'); + moveDown.className = 'move_down'; + moveDown.id = this.domNodeID + '_down'; + moveDown.appendChild(document.createTextNode('\u2193')); + topActionSpan.appendChild(moveDown); + + var moveUp = document.createElement('span'); + moveUp.className = 'move_up'; + moveUp.id = this.domNodeID + '_up'; + moveUp.appendChild(document.createTextNode('\u2191')); + topActionSpan.appendChild(moveUp); + + var deleteButton = document.createElement('span'); + deleteButton.className = 'xml_delete'; + deleteButton.id = this.domNodeID + '_del'; + deleteButton.appendChild(document.createTextNode('X')); + topActionSpan.appendChild(deleteButton); + } return topActionSpan; }; @@ -287,24 +297,24 @@ XMLElement.prototype.addContentContainers = function (recursive) { if (this.allowText) { if (this.allowAttributes) { if (this.allowChildren) { - placeholder.appendChild(document.createTextNode('Use the menu to add subelements, attributes and text.')); + placeholder.appendChild(document.createTextNode(this.editor.options.i18n[this.editor.options.userLang].useMenuToAdd)); } else { - placeholder.appendChild(document.createTextNode('Use the menu to add attributes and text.')); + placeholder.appendChild(document.createTextNode(this.editor.options.i18n[this.editor.options.userLang].useMenuToAddAttributesText)); } } else if (this.allowChildren) { - placeholder.appendChild(document.createTextNode('Use the menu to add subelements and text.')); + placeholder.appendChild(document.createTextNode(this.editor.options.i18n[this.editor.options.userLang].useMenuToAddSubelementsText)); } else { - placeholder.appendChild(document.createTextNode('Use the menu to add text.')); + placeholder.appendChild(document.createTextNode(this.editor.options.i18n[this.editor.options.userLang].useMenuToAddText)); } } else { if (this.allowAttributes) { if (this.allowChildren) { - placeholder.appendChild(document.createTextNode('Use the menu to add subelements and attributes.')); + placeholder.appendChild(document.createTextNode(this.editor.options.i18n[this.editor.options.userLang].useMenuToAddSubelementsAttributes)); } else { - placeholder.appendChild(document.createTextNode('Use the menu to add attributes.')); + placeholder.appendChild(document.createTextNode(this.editor.options.i18n[this.editor.options.userLang].useMenuToAddAttributes)); } } else if (this.allowChildren) { - placeholder.appendChild(document.createTextNode('Use the menu to add subelements.')); + placeholder.appendChild(document.createTextNode(this.editor.options.i18n[this.editor.options.userLang].useMenuToAddSubelements)); } } @@ -683,4 +693,4 @@ XMLElement.prototype.toggleCollapsed = function() { self.domNode.addClass("collapsed"); }); } -}; \ No newline at end of file +}; diff --git a/src/xml_element_stub.js b/src/xml_element_stub.js index 01d88e6..59556aa 100644 --- a/src/xml_element_stub.js +++ b/src/xml_element_stub.js @@ -190,4 +190,4 @@ XMLElementStub.prototype.isSelected = function() { XMLElementStub.prototype.focus = function() { this.nameInput.focus(); -}; \ No newline at end of file +}; diff --git a/src/xml_templates.js b/src/xml_templates.js index 5489516..1272f75 100644 --- a/src/xml_templates.js +++ b/src/xml_templates.js @@ -3,6 +3,7 @@ * @param init_object * @constructor */ + function XMLTemplates(init_object) { this.template_path = init_object.options.templateOptions.templatePath; this.templates = init_object.options.templateOptions.templates; @@ -26,9 +27,9 @@ XMLTemplates.prototype.createDialog = function() { var self = this; var buttons = {}; if (self.editor.options.templateOptions.cancelFunction) { - buttons["Cancel"] = $.proxy(self.editor.options.templateOptions.cancelFunction, self); + buttons[self.editor.options.i18n[self.editor.options.userLang].cancel] = $.proxy(self.editor.options.templateOptions.cancelFunction, self); } - buttons["Choose"] = function() { + buttons[self.editor.options.i18n[self.editor.options.userLang].choose] = function() { self.processForm(); }; @@ -112,7 +113,7 @@ XMLTemplates.prototype.loadSelectedTemplate = function(selection) { var xml_string = self.editor.xml2Str(data); self.editor._documentReady(xml_string); }).fail(function(jqXHR, textStatus) { - alert("Unable to load the requested template: " + textStatus); + alert(self.editor.options.i18n[self.editor.options.userLang].unableToLoadTemplate + textStatus); }); }; diff --git a/src/xml_text_node.js b/src/xml_text_node.js index ec7612f..f983fd5 100644 --- a/src/xml_text_node.js +++ b/src/xml_text_node.js @@ -103,4 +103,5 @@ XMLTextNode.prototype.focus = function() { XMLTextNode.prototype.isSelected = function() { return AbstractXMLObject.prototype.isSelected.call(this); -}; \ No newline at end of file +}; + diff --git a/src/xml_unspecified_element.js b/src/xml_unspecified_element.js index d58094b..c1d65ec 100644 --- a/src/xml_unspecified_element.js +++ b/src/xml_unspecified_element.js @@ -88,7 +88,7 @@ XMLUnspecifiedElement.prototype.addContentContainers = function (recursive) { var placeholder = document.createElement('div'); placeholder.className = 'placeholder'; - placeholder.appendChild(document.createTextNode('Use the menu to add contents.')); + placeholder.appendChild(document.createTextNode(this.editor.options.i18n[this.options.userLang].useMenuToAddContents)); this.contentContainer.appendChild(placeholder); this.placeholder = $(placeholder); @@ -104,4 +104,4 @@ XMLUnspecifiedElement.prototype.updateChildrenCount = function(childElement, del XMLUnspecifiedElement.prototype.childCanBeRemoved = function(childType) { return true; -}; \ No newline at end of file +}; diff --git a/xsd/src/schema_manager.js b/xsd/src/schema_manager.js index c075c62..ebeed15 100644 --- a/xsd/src/schema_manager.js +++ b/xsd/src/schema_manager.js @@ -234,20 +234,51 @@ SchemaManager.prototype.mergeRootLevelElements = function() { SchemaManager.prototype.exportNamespaces = function() { // Add all the namespaces from imported schemas into the registry of namespaces for the root schema - var namespaceRegistry = []; - for (var index in this.namespaceIndexes) { - var namespaceUri = this.namespaceIndexes[index]; - $.each(this.originatingSchema.localNamespaces, function(key, val){ - if (val == namespaceUri) { - namespaceRegistry.push({'prefix' : key, 'uri' : val}); - return false; - } + var self = this; + var namespacePrefixes = {}; + var prefixUsed = {}; + this.addNamespacePrefixes(namespacePrefixes, prefixUsed, {'xs': self.xsNS}); + this.addNamespacePrefixes(namespacePrefixes, prefixUsed, self.originatingSchema.localNamespaces); + this.addNamespacePrefixes(namespacePrefixes, prefixUsed, {'': self.originatingSchema.targetNS}); + for (var targetNS in self.imports) { + self.imports[targetNS].forEach(function(schema) { + self.addNamespacePrefixes(namespacePrefixes, prefixUsed, schema.localNamespaces); }); } + var namespaceRegistry = []; + var anonymousNamespaceIndex = 1; + this.namespaceIndexes.forEach(function(namespaceUri) { + var prefix = namespacePrefixes[namespaceUri]; + if (prefix === undefined) { + while (true) { + prefix = 'ns' + anonymousNamespaceIndex; + anonymousNamespaceIndex++; + if (prefixUsed[prefix] === undefined) { + break; + } + } + } + namespaceRegistry.push({'prefix' : prefix, 'uri' : namespaceUri}); + }); this.originatingRoot.namespaces = namespaceRegistry; }; +SchemaManager.prototype.addNamespacePrefixes = function(namespacePrefixes, prefixUsed, namespaces) { + + for (var prefix in namespaces) { + if (prefixUsed[prefix] !== undefined) { + continue; + } + var namespaceUri = namespaces[prefix]; + if (namespacePrefixes[namespaceUri] !== undefined) { + continue; + } + namespacePrefixes[namespaceUri] = prefix; + prefixUsed[prefix] = true; + } +}; + //Post processing step which recursively walks the schema tree and merges type definitions //into elements that reference them. SchemaManager.prototype.resolveTypeReferences = function(definition) { @@ -274,17 +305,6 @@ SchemaManager.prototype.resolveTypeReferences = function(definition) { } else { // Process children var self = this; - if (definition.elements) { - $.each(definition.elements, function(){ - self.resolveTypeReferences(this); - }); - } - if (definition.attributes != null) { - $.each(definition.attributes, function(){ - self.resolveTypeReferences(this); - }); - } - if (definition.typeRef) { // Merge in any type references var typeRefs = definition.typeRef; @@ -294,17 +314,27 @@ SchemaManager.prototype.resolveTypeReferences = function(definition) { var typeRef = typeRefs[index]; // Find the definition being referenced across all schemas - var typeDef = this.resolveDefinition(typeRef); + var typeDef = this.resolveDefinition(typeRef.indexedName); if (!typeDef) - throw new Error("Could not resolve reference to type " + typeRef + throw new Error("Could not resolve reference to type " + typeRef.indexedName + " from definition " + definition.name); // Compute nested types depth first before merging in this type this.resolveTypeReferences(typeDef); - this.mergeType(definition, typeDef); + this.mergeType(definition, typeDef, typeRef.mergeMode); } } + if (definition.elements) { + $.each(definition.elements, function(){ + self.resolveTypeReferences(this); + }); + } + if (definition.attributes != null) { + $.each(definition.attributes, function(){ + self.resolveTypeReferences(this); + }); + } } }; @@ -342,25 +372,77 @@ SchemaManager.prototype.resolveSchema = function(schema, name) { return this; }; -SchemaManager.prototype.mergeType = function(base, type) { +SchemaManager.prototype.mergeType = function(base, type, mergeMode) { for (var key in type) { if (type.hasOwnProperty(key)) { var value = type[key]; if (value != null && base[key] == null){ base[key] = value; - } else if ($.isArray(value) && $.isArray(type[key])){ - base[key] = base[key].concat(value); + } else if ($.isArray(value) && $.isArray(base[key])){ + base[key] = this.mergeTypeArray(base[key], value, key, mergeMode); } } } }; +SchemaManager.prototype.mergeTypeArray = function(baseArray, typeArray, key, mergeMode) { + var mergeFunction = this['mergeType_' + key + '_' + mergeMode]; + if (mergeFunction === undefined) { + throw Error('Invalid definition key ' + JSON.stringify(key) + ' or merge mode ' + JSON.stringify(mergeMode)); + } + return mergeFunction.call(this, baseArray, typeArray); +}; + +SchemaManager.prototype.mergeType_choices_extension = function(baseArray, typeArray) { + return baseArray.concat(typeArray); +}; + +SchemaManager.prototype.mergeType_choices_restriction = function(baseArray, typeArray) { + return baseArray.concat(typeArray); +}; + +SchemaManager.prototype.mergeType_attributes_extension = function(baseArray, typeArray) { + return baseArray.concat(typeArray); +}; + +SchemaManager.prototype.mergeType_attributes_restriction = function(baseArray, typeArray) { + var baseNames = {}; + for (var i = 0; i < baseArray.length; i++) { + baseNames[baseArray[i].name] = true; + } + var typeResult = []; + for (var j = 0; j < typeArray.length; j++) { + var typeItem = typeArray[j]; + if (!baseNames[typeItem.name]) { + typeResult.push(typeItem); + } + } + return typeResult.concat(baseArray); +}; + +SchemaManager.prototype.mergeType_elements_extension = function(baseArray, typeArray) { + return baseArray.concat(typeArray); +}; + +SchemaManager.prototype.mergeType_elements_restriction = function(baseArray, typeArray) { + return baseArray.concat(typeArray); +}; + +SchemaManager.prototype.mergeType_values_extension = function(baseArray, typeArray) { + return baseArray.concat(typeArray); +}; + +SchemaManager.prototype.mergeType_values_restriction = function(baseArray, typeArray) { + return baseArray; +}; + SchemaManager.prototype.mergeRef = function(base, ref) { if (ref.name) base.name = ref.name; base.ns = ref.ns; - this.mergeType(base, ref); + var mergeMode = 'extension'; + this.mergeType(base, ref, mergeMode); } //Register a namespace if it is new diff --git a/xsd/src/schema_processor.js b/xsd/src/schema_processor.js index 342000a..3b2c9b9 100644 --- a/xsd/src/schema_processor.js +++ b/xsd/src/schema_processor.js @@ -22,22 +22,29 @@ function SchemaProcessor(xsdDocument, xsdManager, schemaUrl, parentNSIndex) { // Local namespace prefix registry this.localNamespaces = $.extend({}, this.xsdManager.globalNamespaces); - this.xsPrefix = "xs:"; - + this.extractNamespaces(); + if (parentNSIndex !== undefined) { this.targetNSIndex = parentNSIndex; this.targetNS = this.xsdManager.getNamespaceUri(parentNSIndex); } else { - // The target namespace for this schema - this.targetNS = null; - // The index of the target namespace in namespaceIndexes - this.targetNSIndex = null; + this.targetNS = this.xsd.getAttribute('targetNamespace'); + this.targetNSIndex = this.xsdManager.registerNamespace(this.targetNS); } + // Register the correct default namespace if none was specified, see: + // * https://www.w3.org/TR/xmlschema11-1/#src-include + // * https://www.w3.org/TR/xmlschema11-1/#sec-src-resolve + if (!('' in this.localNamespaces)) { + if (this.xsd.getAttribute('targetNamespace') === null) { + this.localNamespaces[''] = this.targetNS; + } else { + this.localNamespaces[''] = this.xsdManager.xsNS; + } + } + // Root definition for this schema, either an element or a schema object this.root = null; - - this.extractNamespaces(); }; // Process the schema file to extract its structure into javascript objects @@ -59,41 +66,13 @@ SchemaProcessor.prototype.extractNamespaces = function() { var namespacePrefix = attr.nodeName.substring(5).replace(":", ""); var namespaceUri = attr.nodeValue; this.registerNamespace(namespaceUri, namespacePrefix); - - // Store the namespace prefix for the xs namespace - if (attr.nodeValue == this.xsdManager.xsNS){ - this.xsPrefix = namespacePrefix; - if (this.xsPrefix != "") - this.xsPrefix = this.xsPrefix + ":"; - } - } - } - - // Only use target namespace if not already being provided with a namespace uri - if (!this.targetNS) { - // Store the target namespace of this schema. - this.targetNS = this.xsd.getAttribute("targetNamespace"); - - // Determine if the targetNS is already registered locally - var localPrefix = this.getLocalNamespacePrefix(this.targetNS); - if (localPrefix == null) { - // Register the target namespace as the default namespace - localPrefix = ""; } - this.targetNSIndex = this.registerNamespace(this.targetNS, ""); - } - - // Register the target ns as the default ns if none was specified - if (!("" in this.localNamespaces)) { - this.localNamespaces[""] = this.targetNS; } }; -SchemaProcessor.prototype.createDefinition = function(node, nameParts) { - if (!nameParts) { - var name = node.getAttribute("name"); - if (name) - nameParts = this.extractName(name); +SchemaProcessor.prototype.createDefinition = function(node, name) { + if (!name) { + name = node.getAttribute("name"); } // New root definition @@ -104,8 +83,8 @@ SchemaProcessor.prototype.createDefinition = function(node, nameParts) { np : true }; - if (nameParts && nameParts.localName) - definition.name = nameParts.localName; + if (name) + definition.name = name; if (node.localName == "attribute") { definition.attribute = true; @@ -127,11 +106,11 @@ SchemaProcessor.prototype.addElement = function(node, definition, parentDef) { var minOccurs = node.getAttribute("minOccurs"); var maxOccurs = node.getAttribute("maxOccurs"); if (parentDef && (minOccurs || maxOccurs)) { - var nameOrRef = node.getAttribute("name") || node.getAttribute("ref"); - var nameOrRefParts = this.extractName(nameOrRef); + var name = node.getAttribute('name'); + var indexedNameOrRef = name ? this.indexedDefinitionName(name) : this.extractName(node.getAttribute('ref')).indexedName; if (!("occurs" in parentDef)) parentDef.occurs = {}; - parentDef.occurs[nameOrRefParts.indexedName] = { + parentDef.occurs[indexedNameOrRef] = { 'min' : minOccurs, 'max' : maxOccurs }; @@ -157,13 +136,13 @@ SchemaProcessor.prototype.addReference = function(definition, refName) { definition.ref = nameParts.indexedName; }; -SchemaProcessor.prototype.addTypeReference = function(definition, refName) { +SchemaProcessor.prototype.addTypeReference = function(definition, refName, mergeMode) { var nameParts = this.extractName(refName); if (!definition.typeRef) { definition.typeRef = []; } - definition.typeRef.push(nameParts.indexedName); + definition.typeRef.push({indexedName: nameParts.indexedName, mergeMode: mergeMode}); }; // Build the schema tag @@ -172,7 +151,8 @@ SchemaProcessor.prototype.build_schema = function(node) { elements : [], ns : this.targetNSIndex, schema : true, - np : true + np : true, + use : node.getAttribute("use") /* Patched for auto-add of required attributes. See also jquery.xmleditor.js : GUIEditor.prototype.addAttributeEvent and XMLElement.prototype.populateChildren */ }; var self = this; var children = this.getChildren(node); @@ -196,11 +176,10 @@ SchemaProcessor.prototype.buildTopLevel = function(node) { // Root level definitions must have a name attribute if (!name) return; - var nameParts = this.extractName(name); // New root definition - var definition = this.createDefinition(node, nameParts); - this.rootDefinitions[nameParts.indexedName] = definition; + var definition = this.createDefinition(node, name); + this.rootDefinitions[this.indexedDefinitionName(name)] = definition; return this.build(node, definition); }; @@ -215,22 +194,29 @@ SchemaProcessor.prototype.build = function(node, definition, parentDef) { SchemaProcessor.prototype.build_element = function(node, definition, parentDef) { var ref = node.getAttribute("ref"); - var subGroup = node.getAttribute("substitutionGroup"); if (ref) { this.addReference(definition, ref); - } else if (subGroup) { - this.addTypeReference(definition, subGroup); } else { // Build or retrieve the type definition var type = node.getAttribute("type"); if (type == null) { - this.build(this.getChildren(node)[0], definition); + var child = this.getChildren(node)[0]; + if (child) { + this.build(child, definition); + } else { + var subGroup = node.getAttribute('substitutionGroup'); + if (subGroup) { + this.addTypeReference(definition, subGroup, 'extension'); + } else { + definition.type = 'anyType'; + } + } } else { // Check to see if it is a built in type definition.type = this.getBuiltInType(type, definition); if (definition.type == null) { // Was not built in, make a reference to resolve later - this.addTypeReference(definition, type); + this.addTypeReference(definition, type, 'extension'); } } } @@ -256,7 +242,7 @@ SchemaProcessor.prototype.build_attribute = function(node, definition) { definition.type = this.getBuiltInType(type, definition); if (definition.type == null) { // Was not built in, make a reference to resolve later - this.addTypeReference(definition, type); + this.addTypeReference(definition, type, 'extension'); } } } @@ -297,7 +283,7 @@ SchemaProcessor.prototype.build_simpleType = function(node, definition) { // Process a list tag, which allows for a single tag with multiple values SchemaProcessor.prototype.build_list = function(node, definition) { // For the moment, lists will just be treated as free text fields - definition.type = this.xsPrefix + "string"; + definition.type = "string"; definition.multivalued = true; }; @@ -312,7 +298,7 @@ SchemaProcessor.prototype.build_union = function(node, definition) { definition.type = this.getBuiltInType(memberType, definition); if (definition.type == null) { - this.addTypeReference(definition, memberType); + this.addTypeReference(definition, memberType, 'extension'); } } } @@ -327,7 +313,7 @@ SchemaProcessor.prototype.build_union = function(node, definition) { SchemaProcessor.prototype.build_group = function(node, definition) { var ref = node.getAttribute("ref"); if (ref){ - this.addTypeReference(definition, ref); + this.addTypeReference(definition, ref, 'extension'); return definition; } var self = this; @@ -438,7 +424,7 @@ SchemaProcessor.prototype.build_restriction = function(node, definition) { definition.type = this.getBuiltInType(base, definition); if (definition.type == null) { - this.addTypeReference(definition, base); + this.addTypeReference(definition, base, 'restriction'); } var self = this; @@ -466,7 +452,7 @@ SchemaProcessor.prototype.build_extension = function(node, definition) { definition.type = this.getBuiltInType(base, definition); if (definition.type == null) { - this.addTypeReference(definition, base); + this.addTypeReference(definition, base, 'extension'); } var self = this; @@ -489,7 +475,7 @@ SchemaProcessor.prototype.build_extension = function(node, definition) { SchemaProcessor.prototype.build_attributeGroup = function(node, definition) { var ref = node.getAttribute("ref"); if (ref){ - this.addTypeReference(definition, ref); + this.addTypeReference(definition, ref, 'extension'); return definition; } @@ -510,13 +496,9 @@ SchemaProcessor.prototype.build_attributeGroup = function(node, definition) { SchemaProcessor.prototype.getBuiltInType = function(type, definition) { if (definition.type != null) return definition.type; - if (type.indexOf(":") == -1) { - if (this.xsPrefix == "") - return type; - } else { - if (type.indexOf(this.xsPrefix) == 0){ - return type.substring(this.xsPrefix.length); - } + var nameParts = this.extractName(type); + if (nameParts.namespaceUri === this.xsdManager.xsNS) { + return nameParts.localName; } return null; }; @@ -557,6 +539,10 @@ SchemaProcessor.prototype.containsChild = function(definition, child) { return false; }; +SchemaProcessor.prototype.indexedDefinitionName = function(name) { + return this.targetNSIndex + ':' + name; +}; + SchemaProcessor.prototype.extractName = function(name) { if (!name) return null; @@ -569,7 +555,9 @@ SchemaProcessor.prototype.extractName = function(name) { result['localName'] = name.substring(index + 1); result['prefix'] = name.substring(0, index); } - result['namespace'] = this.xsdManager.getNamespaceIndex(this.localNamespaces[result.prefix]); + var namespaceUri = this.localNamespaces[result.prefix]; + result['namespaceUri'] = namespaceUri; + result['namespace'] = this.xsdManager.getNamespaceIndex(namespaceUri); result['indexedName'] = result.namespace + ":" + result.localName; return result; }; diff --git a/xsd/xsd2json.js b/xsd/xsd2json.js index c7e7457..7443068 100644 --- a/xsd/xsd2json.js +++ b/xsd/xsd2json.js @@ -1,5 +1,5 @@ -;var Xsd2Json = function() { - +//= require_self +//= require_tree . /* Copyright 2008 The University of North Carolina at Chapel Hill @@ -29,11 +29,9 @@ * * @author Ben Pennell */ - function Xsd2Json(originatingXsdName, options) { var defaults = { - schemaURI: "", - rootElement: null + schemaURI: "" }; this.options = $.extend({}, defaults, options); @@ -105,14 +103,12 @@ Xsd2Json.prototype.exportJSON = function(filename, variableName, pretty) { , false, false, false, false, 0, null ); a.dispatchEvent(event); -}; -/** +};/** * Manages processing of a set of schemas to produce a single resulting * tree of elements * * @author bbpennel */ - function SchemaManager(originatingXsdName, options) { var defaults = { globalNamespaces : {} @@ -343,20 +339,51 @@ SchemaManager.prototype.mergeRootLevelElements = function() { SchemaManager.prototype.exportNamespaces = function() { // Add all the namespaces from imported schemas into the registry of namespaces for the root schema - var namespaceRegistry = []; - for (var index in this.namespaceIndexes) { - var namespaceUri = this.namespaceIndexes[index]; - $.each(this.originatingSchema.localNamespaces, function(key, val){ - if (val == namespaceUri) { - namespaceRegistry.push({'prefix' : key, 'uri' : val}); - return false; - } + var self = this; + var namespacePrefixes = {}; + var prefixUsed = {}; + this.addNamespacePrefixes(namespacePrefixes, prefixUsed, {'xs': self.xsNS}); + this.addNamespacePrefixes(namespacePrefixes, prefixUsed, self.originatingSchema.localNamespaces); + this.addNamespacePrefixes(namespacePrefixes, prefixUsed, {'': self.originatingSchema.targetNS}); + for (var targetNS in self.imports) { + self.imports[targetNS].forEach(function(schema) { + self.addNamespacePrefixes(namespacePrefixes, prefixUsed, schema.localNamespaces); }); } + var namespaceRegistry = []; + var anonymousNamespaceIndex = 1; + this.namespaceIndexes.forEach(function(namespaceUri) { + var prefix = namespacePrefixes[namespaceUri]; + if (prefix === undefined) { + while (true) { + prefix = 'ns' + anonymousNamespaceIndex; + anonymousNamespaceIndex++; + if (prefixUsed[prefix] === undefined) { + break; + } + } + } + namespaceRegistry.push({'prefix' : prefix, 'uri' : namespaceUri}); + }); this.originatingRoot.namespaces = namespaceRegistry; }; +SchemaManager.prototype.addNamespacePrefixes = function(namespacePrefixes, prefixUsed, namespaces) { + + for (var prefix in namespaces) { + if (prefixUsed[prefix] !== undefined) { + continue; + } + var namespaceUri = namespaces[prefix]; + if (namespacePrefixes[namespaceUri] !== undefined) { + continue; + } + namespacePrefixes[namespaceUri] = prefix; + prefixUsed[prefix] = true; + } +}; + //Post processing step which recursively walks the schema tree and merges type definitions //into elements that reference them. SchemaManager.prototype.resolveTypeReferences = function(definition) { @@ -383,17 +410,6 @@ SchemaManager.prototype.resolveTypeReferences = function(definition) { } else { // Process children var self = this; - if (definition.elements) { - $.each(definition.elements, function(){ - self.resolveTypeReferences(this); - }); - } - if (definition.attributes != null) { - $.each(definition.attributes, function(){ - self.resolveTypeReferences(this); - }); - } - if (definition.typeRef) { // Merge in any type references var typeRefs = definition.typeRef; @@ -403,17 +419,27 @@ SchemaManager.prototype.resolveTypeReferences = function(definition) { var typeRef = typeRefs[index]; // Find the definition being referenced across all schemas - var typeDef = this.resolveDefinition(typeRef); + var typeDef = this.resolveDefinition(typeRef.indexedName); if (!typeDef) - throw new Error("Could not resolve reference to type " + typeRef + throw new Error("Could not resolve reference to type " + typeRef.indexedName + " from definition " + definition.name); // Compute nested types depth first before merging in this type this.resolveTypeReferences(typeDef); - this.mergeType(definition, typeDef); + this.mergeType(definition, typeDef, typeRef.mergeMode); } } + if (definition.elements) { + $.each(definition.elements, function(){ + self.resolveTypeReferences(this); + }); + } + if (definition.attributes != null) { + $.each(definition.attributes, function(){ + self.resolveTypeReferences(this); + }); + } } }; @@ -451,25 +477,77 @@ SchemaManager.prototype.resolveSchema = function(schema, name) { return this; }; -SchemaManager.prototype.mergeType = function(base, type) { +SchemaManager.prototype.mergeType = function(base, type, mergeMode) { for (var key in type) { if (type.hasOwnProperty(key)) { var value = type[key]; if (value != null && base[key] == null){ base[key] = value; - } else if ($.isArray(value) && $.isArray(type[key])){ - base[key] = base[key].concat(value); + } else if ($.isArray(value) && $.isArray(base[key])){ + base[key] = this.mergeTypeArray(base[key], value, key, mergeMode); } } } }; +SchemaManager.prototype.mergeTypeArray = function(baseArray, typeArray, key, mergeMode) { + var mergeFunction = this['mergeType_' + key + '_' + mergeMode]; + if (mergeFunction === undefined) { + throw Error('Invalid definition key ' + JSON.stringify(key) + ' or merge mode ' + JSON.stringify(mergeMode)); + } + return mergeFunction.call(this, baseArray, typeArray); +}; + +SchemaManager.prototype.mergeType_choices_extension = function(baseArray, typeArray) { + return baseArray.concat(typeArray); +}; + +SchemaManager.prototype.mergeType_choices_restriction = function(baseArray, typeArray) { + return baseArray.concat(typeArray); +}; + +SchemaManager.prototype.mergeType_attributes_extension = function(baseArray, typeArray) { + return baseArray.concat(typeArray); +}; + +SchemaManager.prototype.mergeType_attributes_restriction = function(baseArray, typeArray) { + var baseNames = {}; + for (var i = 0; i < baseArray.length; i++) { + baseNames[baseArray[i].name] = true; + } + var typeResult = []; + for (var j = 0; j < typeArray.length; j++) { + var typeItem = typeArray[j]; + if (!baseNames[typeItem.name]) { + typeResult.push(typeItem); + } + } + return typeResult.concat(baseArray); +}; + +SchemaManager.prototype.mergeType_elements_extension = function(baseArray, typeArray) { + return baseArray.concat(typeArray); +}; + +SchemaManager.prototype.mergeType_elements_restriction = function(baseArray, typeArray) { + return baseArray.concat(typeArray); +}; + +SchemaManager.prototype.mergeType_values_extension = function(baseArray, typeArray) { + return baseArray.concat(typeArray); +}; + +SchemaManager.prototype.mergeType_values_restriction = function(baseArray, typeArray) { + return baseArray; +}; + SchemaManager.prototype.mergeRef = function(base, ref) { if (ref.name) base.name = ref.name; base.ns = ref.ns; - this.mergeType(base, ref); + var mergeMode = 'extension'; + this.mergeType(base, ref, mergeMode); } //Register a namespace if it is new @@ -489,8 +567,7 @@ SchemaManager.prototype.getNamespaceIndex = function(namespaceUri) { SchemaManager.prototype.getNamespaceUri = function(index) { return this.namespaceIndexes[index]; -}; -/** +};/** * Processes an XML schema, extracting key features and storing them into * javascript structures. * @@ -501,7 +578,6 @@ SchemaManager.prototype.getNamespaceUri = function(index) { * xsdDocument XML document representaton of the schema document to be processed * xsdManager schema manager which this processor belongs to */ - function SchemaProcessor(xsdDocument, xsdManager, schemaUrl, parentNSIndex) { this.xsd = xsdDocument; @@ -515,22 +591,29 @@ function SchemaProcessor(xsdDocument, xsdManager, schemaUrl, parentNSIndex) { // Local namespace prefix registry this.localNamespaces = $.extend({}, this.xsdManager.globalNamespaces); - this.xsPrefix = "xs:"; - + this.extractNamespaces(); + if (parentNSIndex !== undefined) { this.targetNSIndex = parentNSIndex; this.targetNS = this.xsdManager.getNamespaceUri(parentNSIndex); } else { - // The target namespace for this schema - this.targetNS = null; - // The index of the target namespace in namespaceIndexes - this.targetNSIndex = null; + this.targetNS = this.xsd.getAttribute('targetNamespace'); + this.targetNSIndex = this.xsdManager.registerNamespace(this.targetNS); } + // Register the correct default namespace if none was specified, see: + // * https://www.w3.org/TR/xmlschema11-1/#src-include + // * https://www.w3.org/TR/xmlschema11-1/#sec-src-resolve + if (!('' in this.localNamespaces)) { + if (this.xsd.getAttribute('targetNamespace') === null) { + this.localNamespaces[''] = this.targetNS; + } else { + this.localNamespaces[''] = this.xsdManager.xsNS; + } + } + // Root definition for this schema, either an element or a schema object this.root = null; - - this.extractNamespaces(); }; // Process the schema file to extract its structure into javascript objects @@ -552,41 +635,13 @@ SchemaProcessor.prototype.extractNamespaces = function() { var namespacePrefix = attr.nodeName.substring(5).replace(":", ""); var namespaceUri = attr.nodeValue; this.registerNamespace(namespaceUri, namespacePrefix); - - // Store the namespace prefix for the xs namespace - if (attr.nodeValue == this.xsdManager.xsNS){ - this.xsPrefix = namespacePrefix; - if (this.xsPrefix != "") - this.xsPrefix = this.xsPrefix + ":"; - } - } - } - - // Only use target namespace if not already being provided with a namespace uri - if (!this.targetNS) { - // Store the target namespace of this schema. - this.targetNS = this.xsd.getAttribute("targetNamespace"); - - // Determine if the targetNS is already registered locally - var localPrefix = this.getLocalNamespacePrefix(this.targetNS); - if (localPrefix == null) { - // Register the target namespace as the default namespace - localPrefix = ""; } - this.targetNSIndex = this.registerNamespace(this.targetNS, ""); - } - - // Register the target ns as the default ns if none was specified - if (!("" in this.localNamespaces)) { - this.localNamespaces[""] = this.targetNS; } }; -SchemaProcessor.prototype.createDefinition = function(node, nameParts) { - if (!nameParts) { - var name = node.getAttribute("name"); - if (name) - nameParts = this.extractName(name); +SchemaProcessor.prototype.createDefinition = function(node, name) { + if (!name) { + name = node.getAttribute("name"); } // New root definition @@ -597,8 +652,8 @@ SchemaProcessor.prototype.createDefinition = function(node, nameParts) { np : true }; - if (nameParts && nameParts.localName) - definition.name = nameParts.localName; + if (name) + definition.name = name; if (node.localName == "attribute") { definition.attribute = true; @@ -620,11 +675,11 @@ SchemaProcessor.prototype.addElement = function(node, definition, parentDef) { var minOccurs = node.getAttribute("minOccurs"); var maxOccurs = node.getAttribute("maxOccurs"); if (parentDef && (minOccurs || maxOccurs)) { - var nameOrRef = node.getAttribute("name") || node.getAttribute("ref"); - var nameOrRefParts = this.extractName(nameOrRef); + var name = node.getAttribute('name'); + var indexedNameOrRef = name ? this.indexedDefinitionName(name) : this.extractName(node.getAttribute('ref')).indexedName; if (!("occurs" in parentDef)) parentDef.occurs = {}; - parentDef.occurs[nameOrRefParts.indexedName] = { + parentDef.occurs[indexedNameOrRef] = { 'min' : minOccurs, 'max' : maxOccurs }; @@ -650,13 +705,13 @@ SchemaProcessor.prototype.addReference = function(definition, refName) { definition.ref = nameParts.indexedName; }; -SchemaProcessor.prototype.addTypeReference = function(definition, refName) { +SchemaProcessor.prototype.addTypeReference = function(definition, refName, mergeMode) { var nameParts = this.extractName(refName); if (!definition.typeRef) { definition.typeRef = []; } - definition.typeRef.push(nameParts.indexedName); + definition.typeRef.push({indexedName: nameParts.indexedName, mergeMode: mergeMode}); }; // Build the schema tag @@ -665,7 +720,8 @@ SchemaProcessor.prototype.build_schema = function(node) { elements : [], ns : this.targetNSIndex, schema : true, - np : true + np : true, + use : node.getAttribute("use") /* Patched for auto-add of required attributes. See also jquery.xmleditor.js : GUIEditor.prototype.addAttributeEvent and XMLElement.prototype.populateChildren */ }; var self = this; var children = this.getChildren(node); @@ -689,11 +745,10 @@ SchemaProcessor.prototype.buildTopLevel = function(node) { // Root level definitions must have a name attribute if (!name) return; - var nameParts = this.extractName(name); // New root definition - var definition = this.createDefinition(node, nameParts); - this.rootDefinitions[nameParts.indexedName] = definition; + var definition = this.createDefinition(node, name); + this.rootDefinitions[this.indexedDefinitionName(name)] = definition; return this.build(node, definition); }; @@ -708,22 +763,29 @@ SchemaProcessor.prototype.build = function(node, definition, parentDef) { SchemaProcessor.prototype.build_element = function(node, definition, parentDef) { var ref = node.getAttribute("ref"); - var subGroup = node.getAttribute("substitutionGroup"); if (ref) { this.addReference(definition, ref); - } else if (subGroup) { - this.addTypeReference(definition, subGroup); } else { // Build or retrieve the type definition var type = node.getAttribute("type"); if (type == null) { - this.build(this.getChildren(node)[0], definition); + var child = this.getChildren(node)[0]; + if (child) { + this.build(child, definition); + } else { + var subGroup = node.getAttribute('substitutionGroup'); + if (subGroup) { + this.addTypeReference(definition, subGroup, 'extension'); + } else { + definition.type = 'anyType'; + } + } } else { // Check to see if it is a built in type definition.type = this.getBuiltInType(type, definition); if (definition.type == null) { // Was not built in, make a reference to resolve later - this.addTypeReference(definition, type); + this.addTypeReference(definition, type, 'extension'); } } } @@ -749,7 +811,7 @@ SchemaProcessor.prototype.build_attribute = function(node, definition) { definition.type = this.getBuiltInType(type, definition); if (definition.type == null) { // Was not built in, make a reference to resolve later - this.addTypeReference(definition, type); + this.addTypeReference(definition, type, 'extension'); } } } @@ -790,7 +852,7 @@ SchemaProcessor.prototype.build_simpleType = function(node, definition) { // Process a list tag, which allows for a single tag with multiple values SchemaProcessor.prototype.build_list = function(node, definition) { // For the moment, lists will just be treated as free text fields - definition.type = this.xsPrefix + "string"; + definition.type = "string"; definition.multivalued = true; }; @@ -805,7 +867,7 @@ SchemaProcessor.prototype.build_union = function(node, definition) { definition.type = this.getBuiltInType(memberType, definition); if (definition.type == null) { - this.addTypeReference(definition, memberType); + this.addTypeReference(definition, memberType, 'extension'); } } } @@ -820,7 +882,7 @@ SchemaProcessor.prototype.build_union = function(node, definition) { SchemaProcessor.prototype.build_group = function(node, definition) { var ref = node.getAttribute("ref"); if (ref){ - this.addTypeReference(definition, ref); + this.addTypeReference(definition, ref, 'extension'); return definition; } var self = this; @@ -931,7 +993,7 @@ SchemaProcessor.prototype.build_restriction = function(node, definition) { definition.type = this.getBuiltInType(base, definition); if (definition.type == null) { - this.addTypeReference(definition, base); + this.addTypeReference(definition, base, 'restriction'); } var self = this; @@ -959,7 +1021,7 @@ SchemaProcessor.prototype.build_extension = function(node, definition) { definition.type = this.getBuiltInType(base, definition); if (definition.type == null) { - this.addTypeReference(definition, base); + this.addTypeReference(definition, base, 'extension'); } var self = this; @@ -982,7 +1044,7 @@ SchemaProcessor.prototype.build_extension = function(node, definition) { SchemaProcessor.prototype.build_attributeGroup = function(node, definition) { var ref = node.getAttribute("ref"); if (ref){ - this.addTypeReference(definition, ref); + this.addTypeReference(definition, ref, 'extension'); return definition; } @@ -1003,13 +1065,9 @@ SchemaProcessor.prototype.build_attributeGroup = function(node, definition) { SchemaProcessor.prototype.getBuiltInType = function(type, definition) { if (definition.type != null) return definition.type; - if (type.indexOf(":") == -1) { - if (this.xsPrefix == "") - return type; - } else { - if (type.indexOf(this.xsPrefix) == 0){ - return type.substring(this.xsPrefix.length); - } + var nameParts = this.extractName(type); + if (nameParts.namespaceUri === this.xsdManager.xsNS) { + return nameParts.localName; } return null; }; @@ -1050,6 +1108,10 @@ SchemaProcessor.prototype.containsChild = function(definition, child) { return false; }; +SchemaProcessor.prototype.indexedDefinitionName = function(name) { + return this.targetNSIndex + ':' + name; +}; + SchemaProcessor.prototype.extractName = function(name) { if (!name) return null; @@ -1062,7 +1124,9 @@ SchemaProcessor.prototype.extractName = function(name) { result['localName'] = name.substring(index + 1); result['prefix'] = name.substring(0, index); } - result['namespace'] = this.xsdManager.getNamespaceIndex(this.localNamespaces[result.prefix]); + var namespaceUri = this.localNamespaces[result.prefix]; + result['namespaceUri'] = namespaceUri; + result['namespace'] = this.xsdManager.getNamespaceIndex(namespaceUri); result['indexedName'] = result.namespace + ":" + result.localName; return result; }; @@ -1083,5 +1147,4 @@ SchemaProcessor.prototype.getLocalNamespacePrefix = function(namespaceUri) { return prefix; } return null; -}; -; return Xsd2Json;}.call(); \ No newline at end of file +}; \ No newline at end of file From 22693397c4328d59d5b360edb77663e47bc10cf6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=B6schele?= Date: Thu, 22 Feb 2018 13:50:24 +0100 Subject: [PATCH 2/9] Updated documentation for how to use the translation --- README.md | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/README.md b/README.md index f6f27a0..9f87d05 100644 --- a/README.md +++ b/README.md @@ -166,6 +166,21 @@ Alternatively, any number of buttons can be created by providing the submitButto Providing a submitButtonConfigs option will override the creation of the standard submit/export button. +#### Translated UI + +Use URL parameters or the userLang parameter of the editor options to switch between the available UI languages "en" (original English) or "de" (German translation). + +Two options: Add URL parameter lang= +.../index.html?lang=de + +or set target language in the options: + +```javascript +$("#xml_editor").xmlEditor({ + userLang : "de" +}); +``` + #### Other editor configuration - documentTitle - Title for the document being edited, which is displayed at the top of the editor. From 34243e97eb577b8e5b65e7b6f76740520c5a9182 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=B6schele?= Date: Thu, 1 Mar 2018 18:51:07 +0100 Subject: [PATCH 3/9] Updated to the comments of the push to UNC master review. --- README.md | 6 + demo/mods_de.html | 48 ++++++ demo/requiredAttribute/testmodel.xsd | 34 ++++ demo/requiredAttribute/testmodel_example.xml | 6 + demo/requiredAttributes.html | 54 +++++++ jquery.xmleditor.js | 159 ++++++++++--------- src/abstract_xml_object.js | 7 +- src/jquery.xmleditor.js | 79 ++++----- src/menu_bar.js | 46 +++++- src/modify_element_menu.js | 1 - src/modify_menu_panel.js | 1 - src/undo_history.js | 1 - src/xml_attribute.js | 4 +- src/xml_element.js | 17 +- src/xml_templates.js | 1 - src/xml_unspecified_element.js | 2 +- xsd/src/schema_processor.js | 3 +- xsd/xsd2json.js | 3 +- 18 files changed, 328 insertions(+), 144 deletions(-) create mode 100644 demo/mods_de.html create mode 100644 demo/requiredAttribute/testmodel.xsd create mode 100644 demo/requiredAttribute/testmodel_example.xml create mode 100644 demo/requiredAttributes.html diff --git a/README.md b/README.md index 9e1b8b1..b3d882a 100644 --- a/README.md +++ b/README.md @@ -6,6 +6,8 @@ Additionally, this project includes a tool for generating JSON objects from XML Try it out in our [jquery.xmleditor demo page](http://unc-libraries.github.com/jquery.xmleditor) using the MODS 3.4 schema. And here's a [demo starting from an existing xml file](http://unc-libraries.github.com/jquery.xmleditor/demo/mods.html) +And here's a [demo as above, but with translated UI](http://unc-libraries.github.com/jquery.xmleditor/demo/mods_de.html) +And here's a [demo as above, but with translated UI and forced required attributes](http://unc-libraries.github.com/jquery.xmleditor/demo/requiredAttributes.html) This project was developed as a part of the [Carolina Digital Repository](https://cdr.lib.unc.edu/) for use in our administrative tools, but is fully functional as a standalone client. This tool is intended to serve as a general schema driven XML editor that runs in web browsers, although some default behaviors are oriented towards it. @@ -193,6 +195,8 @@ $("#xml_editor").xmlEditor({ - sourceDesignSwitch - This is true by default - allows to hide the switch between Text and XML display - initialEditMode - Switch to predefined view: XML or HTML-DIV editor. 0=HTML-DIV editor, 1=XML Source editor - i18n - This is a map in form ["en"] or ["de"] which holds all the labels used in the application for translation. More translations can be added. +- userLang - This selects the map to be used from i18n for the UI translation. +- enforceRequired - This does render required attributes to get no (x) button for remove and will automatically create the attributes. Works only together with enforceOccurs: true. ### Interacting with the editor @@ -230,6 +234,8 @@ Authors [Volker Diels-Grabsch (m-click.aero)](https://github.com/m-click) +[Marc Höschele](https://github.com/mhoesche) + Attribution ------ diff --git a/demo/mods_de.html b/demo/mods_de.html new file mode 100644 index 0000000..b148a00 --- /dev/null +++ b/demo/mods_de.html @@ -0,0 +1,48 @@ + + + + +jQuery.xmleditor MODS Document Editing Demo + + + + + + + + + + + + +
            +
            +

            jQuery.xmleditor Spoonful of Math MODS Editing Demo

            + View + the Project on GitHub UNC-Libraries/jquery.xmleditor + +
            +
            +
            +
            + + + + diff --git a/demo/requiredAttribute/testmodel.xsd b/demo/requiredAttribute/testmodel.xsd new file mode 100644 index 0000000..cda42d7 --- /dev/null +++ b/demo/requiredAttribute/testmodel.xsd @@ -0,0 +1,34 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/demo/requiredAttribute/testmodel_example.xml b/demo/requiredAttribute/testmodel_example.xml new file mode 100644 index 0000000..ab5dbfd --- /dev/null +++ b/demo/requiredAttribute/testmodel_example.xml @@ -0,0 +1,6 @@ + + + + Test + + \ No newline at end of file diff --git a/demo/requiredAttributes.html b/demo/requiredAttributes.html new file mode 100644 index 0000000..10b93b4 --- /dev/null +++ b/demo/requiredAttributes.html @@ -0,0 +1,54 @@ + + + + +jQuery.xmleditor handle required attributes demo + + + + + + + + + + + + + +
            +
            +

            jQuery.xmleditor some sample XML with required attributes

            + View + the Project on GitHub UNC-Libraries/jquery.xmleditor + +
            +
            +
            +
            + + + + diff --git a/jquery.xmleditor.js b/jquery.xmleditor.js index 84e2306..fb15ccd 100644 --- a/jquery.xmleditor.js +++ b/jquery.xmleditor.js @@ -75,30 +75,6 @@ var localName = function(node) { return node.nodeName.substring(index + 1); }; -// Extracts and returns URL parameters or the given default. -function getParam(name, def) { - name = name.replace(/[\[]/, "\\[").replace(/[\]]/, "\\]"); - var regex = new RegExp("[\\?&]" + name + "=([^&#]*)"), - results = regex.exec(location.search); - return results === null ? def : decodeURIComponent(results[1].replace(/\+/g, " ")); -} - -// Add method to String. -String.prototype.replaceAll = function(search, replace) { - //if replace is null, return original string otherwise it will - //replace search string with 'undefined'. - if(!replace) - return this; - return this.replace(new RegExp('[' + search + ']', 'g'), replace); -}; - -// Extract the user language first from the browsers default and let it overwrite via URL parameter "lang=". -var userLang = navigator.language || navigator.userLanguage; -userLang = getParam("lang", userLang); -if ("de" != userLang && "en" != userLang) { - userLang = "en"; -} - $.widget( "xml.xmlEditor", { options: { // Schema object to be used @@ -198,8 +174,8 @@ $.widget( "xml.xmlEditor", { noDocument:"Could not load specified document and no fallback provided, cannot start.", noStartingDocument:"No starting document.", failedToAddChild:"Failed to add child of type ", - undo:"ctrl+z", - redo:"ctrl+y", + ctrlZ:"ctrl+z", + ctrlY:"ctrl+y", del:"del", enter:"enter", elementUp:"alt+up", @@ -214,7 +190,7 @@ $.widget( "xml.xmlEditor", { shiftLeft:"shift+left", shiftRight:"shift+right", altA:"alt+a", - ltShiftE:"alt+shift+e", + altShiftE:"alt+shift+e", altShiftX:"alt+shift+x", altShiftS:"alt+shift+s", altShiftT:"alt+shift+t", @@ -323,8 +299,8 @@ $.widget( "xml.xmlEditor", { noDocument:"Das angegebene Dokument konnte nicht geladen werden und es ist kein Rückfalldokument definiert. Der Editor kann nicht starten.", noStartingDocument:"Es ist kein Startdokument definiert.", failedToAddChild:"Das Element des folgenden Typs konnte nicht hinzugefügt werden: ", - undo:"Strg+z", - redo:"Strg+y", + ctrlZ:"Strg+z", + ctrlY:"Strg+y", del:"Entf", enter:"Enter", elementUp:"Alt+oben", @@ -419,7 +395,9 @@ $.widget( "xml.xmlEditor", { } }, - userLang: getParam("lang", "en") + userLang: "en", + + enforceRequired: false }, @@ -611,8 +589,6 @@ $.widget( "xml.xmlEditor", { // Start loading the document for editing this.loadDocument(this.options.ajaxOptions, localXMLContent); - var editor = this.activeEditor; - setTimeout(function () { if (!!editor['selectNext']) { editor.selectNext(); } }, 200); }, // Load the schema object @@ -870,7 +846,7 @@ $.widget( "xml.xmlEditor", { }, addChildElement: function(parentElement, newElementDefinition, relativeTo, prepend) { - if (!parentElement.allowChildren) + if (!parentElement.allowChildren || !this.activeEditor.editor.options.enableEdit) return null; // If in the text editor view, synchronous the text to the xml model and ensure wellformedness @@ -945,6 +921,11 @@ $.widget( "xml.xmlEditor", { addAttribute: function(xmlElement, attrDefinition, instigator) { + // if we are in readonly mode, return without changing anything + if (!this.activeEditor.editor.options.enableEdit) { + return; + } + // Synchronize xml document if there are unsynchronized changes in the text editor if (this.xmlState.changesNotSynced()) { try { @@ -1008,6 +989,11 @@ $.widget( "xml.xmlEditor", { addNode: function(parentElement, nodeType, prepend, relativeTo) { + // if we are in readonly mode, return without changing anything + if (!this.activeEditor.editor.options.enableEdit) { + return; + } + // Synchronize xml document if there are unsynchronized changes in the text editor if (this.xmlState.changesNotSynced()) { try { @@ -1076,10 +1062,10 @@ $.widget( "xml.xmlEditor", { this.activeEditor = this.guiEditor; var editor = this.activeEditor; setTimeout(function() {editor.selectNext();}, 200); - $("#" + xmlMenuHeaderPrefix + this.options.i18n[this.options.userLang].xml.replace(/ /g, "_")).addClass("active_mode_tab"); + $("#" + xmlMenuHeaderPrefix + 'xml').addClass("active_mode_tab"); } else { this.activeEditor = this.textEditor; - $("#" + xmlMenuHeaderPrefix + this.options.i18n[this.options.userLang].text.replace(/ /g, "_")).addClass("active_mode_tab"); + $("#" + xmlMenuHeaderPrefix + 'text').addClass("active_mode_tab"); } this.activeEditor.activate(); if (this.ready) @@ -1505,31 +1491,22 @@ $.widget( "xml.xmlEditor", { // Menu Update functions refreshMenuUndo: function(self) { if (self.undoHistory.headIndex > 0) { - $("#" + xmlMenuHeaderPrefix + self.options.i18n[self.options.userLang].undoMenuitem.replaceAll(' ','_')).removeClass("disabled").data("menuItemData").enabled = true; + $("#" + xmlMenuHeaderPrefix + 'undoMenuitem').removeClass("disabled").data("menuItemData").enabled = true; } else { - $("#" + xmlMenuHeaderPrefix + self.options.i18n[self.options.userLang].undoMenuitem.replaceAll(' ','_')).addClass("disabled").data("menuItemData").enabled = false; + $("#" + xmlMenuHeaderPrefix + 'undoMenuitem').addClass("disabled").data("menuItemData").enabled = false; } if (self.undoHistory.headIndex < self.undoHistory.states.length - 1) { - $("#" + xmlMenuHeaderPrefix + self.options.i18n[self.options.userLang].redoMenuitem.replaceAll(' ','_')).removeClass("disabled").data("menuItemData").enabled = true; + $("#" + xmlMenuHeaderPrefix + 'redoMenuitem').removeClass("disabled").data("menuItemData").enabled = true; } else { - $("#" + xmlMenuHeaderPrefix + self.options.i18n[self.options.userLang].redoMenuitem.replaceAll(' ','_')).addClass("disabled").data("menuItemData").enabled = false; + $("#" + xmlMenuHeaderPrefix + 'redoMenuitem').addClass("disabled").data("menuItemData").enabled = false; } }, // Performs updates to the menu for changing element/attribute selection refreshMenuSelected: function(self) { - var suffixes = [self.options.i18n[self.options.userLang].deselect.replaceAll(' ','_'), - self.options.i18n[self.options.userLang].nextElement.replaceAll(' ','_'), - self.options.i18n[self.options.userLang].previousElement.replaceAll(' ','_'), - self.options.i18n[self.options.userLang].parentElement.replaceAll(' ','_'), - self.options.i18n[self.options.userLang].firstChild.replaceAll(' ','_'), - self.options.i18n[self.options.userLang].nextSibling.replaceAll(' ','_'), - self.options.i18n[self.options.userLang].previousSibling.replaceAll(' ','_'), - self.options.i18n[self.options.userLang].nextAttribute.replaceAll(' ','_'), - self.options.i18n[self.options.userLang].previousAttribute.replaceAll(' ','_'), - self.options.i18n[self.options.userLang].deleteElement.replaceAll(' ','_'), - self.options.i18n[self.options.userLang].moveElementUp.replaceAll(' ','_'), - self.options.i18n[self.options.userLang].moveElementDown.replaceAll(' ','_')]; + var suffixes = ['deselect', 'nextElement', 'previousElement', 'parentElement', 'firstChild', + 'nextSibling', 'previousSibling', 'nextAttribute', 'previousAttribute', 'deleteElement', + 'moveElementUp', 'moveElementDown']; var hasSelected = self.guiEditor.selectedElement != null && self.guiEditor.active; $.each(suffixes, function(){ if (hasSelected) @@ -1648,8 +1625,11 @@ AbstractXMLObject.prototype.createElementInput = function (inputID, startingValu // Set width of generated fields manually. if (this.objectType.attribute && startingValue) { var len = startingValue.length * 10; - if (len < 80) { len = 80; } - else if (len > 600) { len = 600; } + if (len < 80) { + len = 80; + } else if (len > 600) { + len = 600; + } if (len != -1) { input.style.width = len + "px"; } @@ -2824,6 +2804,7 @@ function MenuBar(editor) { show: true, action : function(event) {self.activateMenu(event);}, items : [ { + id: 'submitChanges', label : self.editor.options.i18n[self.editor.options.userLang].submitChanges, enabled : defaultSubmitConfig != null, show: self.editor.options.enableEdit, @@ -2832,6 +2813,7 @@ function MenuBar(editor) { self.editor.uploadXML.call(self.editor, defaultSubmitConfig); } }, { + id: 'export', label : self.editor.options.i18n[self.editor.options.userLang].export, enabled : (typeof(Blob) !== undefined), show: true, @@ -2839,27 +2821,31 @@ function MenuBar(editor) { action : $.proxy(self.editor.exportXML, self.editor) } ] }, { + id: 'edit', label : self.editor.options.i18n[self.editor.options.userLang].edit, enabled : self.editor.options.enableEdit, // readonly mode show: self.editor.options.enableEdit, // readonly mode action : function(event) {self.activateMenu(event);}, items : [ { + id: 'undoMenuitem', label : self.editor.options.i18n[self.editor.options.userLang].undoMenuitem, enabled : true, show: true, - binding : self.editor.options.i18n[self.editor.options.userLang].undo, + binding : self.editor.options.i18n[self.editor.options.userLang].ctrlZ, action : function() { self.editor.undoHistory.changeHead(-1); } }, { + id: 'redoMenuitem', label : self.editor.options.i18n[self.editor.options.userLang].redoMenuitem, enabled : true, show: true, - binding : self.editor.options.i18n[self.editor.options.userLang].redo, + binding : self.editor.options.i18n[self.editor.options.userLang].ctrlY, action : function() { self.editor.undoHistory.changeHead(1); } }, { + id: 'deleteElement', label : self.editor.options.i18n[self.editor.options.userLang].deleteElement, enabled : true, show: true, @@ -2868,6 +2854,7 @@ function MenuBar(editor) { self.editor.guiEditor.deleteSelected(); } }, { + id: 'moveElementUp', label : self.editor.options.i18n[self.editor.options.userLang].moveElementUp, enabled : true, show: true, @@ -2876,6 +2863,7 @@ function MenuBar(editor) { self.editor.guiEditor.moveSelected(true); } }, { + id: 'moveElementDown', label : self.editor.options.i18n[self.editor.options.userLang].moveElementDown, enabled : true, show: true, @@ -2885,11 +2873,13 @@ function MenuBar(editor) { } } ] }, { + id: 'select', label : self.editor.options.i18n[self.editor.options.userLang].select, enabled : self.editor.options.enableEdit, // readonly mode show: self.editor.options.enableEdit, // readonly mode action : function(event) {self.activateMenu(event);}, items : [ { + id: 'deselect', label : self.editor.options.i18n[self.editor.options.userLang].deselect, enabled : true, show: true, @@ -2898,6 +2888,7 @@ function MenuBar(editor) { self.editor.guiEditor.deselect(); } },{ + id: 'nextElement', label : self.editor.options.i18n[self.editor.options.userLang].nextElement, enabled : true, show: true, @@ -2906,6 +2897,7 @@ function MenuBar(editor) { self.editor.guiEditor.selectNext(); } }, { + id: 'previousElement', label : self.editor.options.i18n[self.editor.options.userLang].previousElement, enabled : true, show: true, @@ -2914,6 +2906,7 @@ function MenuBar(editor) { self.editor.guiEditor.selectNext(true); } }, { + id: 'nextAttribute', label : self.editor.options.i18n[self.editor.options.userLang].nextAttribute, enabled : true, show: true, @@ -2922,6 +2915,7 @@ function MenuBar(editor) { self.editor.guiEditor.selectAttribute(); } }, { + id: 'previousAttribute', label : self.editor.options.i18n[self.editor.options.userLang].previousAttribute, enabled : true, show: true, @@ -2930,6 +2924,7 @@ function MenuBar(editor) { self.editor.guiEditor.selectAttribute(true); } }, { + id: 'parentElement', label : self.editor.options.i18n[self.editor.options.userLang].parentElement, enabled : true, show: true, @@ -2938,6 +2933,7 @@ function MenuBar(editor) { self.editor.guiEditor.selectParent(); } }, { + id: 'firstChild', label : self.editor.options.i18n[self.editor.options.userLang].firstChild, enabled : true, show: true, @@ -2946,6 +2942,7 @@ function MenuBar(editor) { self.editor.guiEditor.selectParent(true); } }, { + id: 'nextSibling', label : self.editor.options.i18n[self.editor.options.userLang].nextSibling, enabled : true, show: true, @@ -2954,6 +2951,7 @@ function MenuBar(editor) { self.editor.guiEditor.selectSibling(); } }, { + id: 'previousSibling', label : self.editor.options.i18n[self.editor.options.userLang].previousSibling, enabled : true, show: true, @@ -2963,11 +2961,13 @@ function MenuBar(editor) { } } ] }, { + id: 'insert', label : self.editor.options.i18n[self.editor.options.userLang].insert, enabled : true, show: self.editor.options.enableEdit, action : function(event) {self.activateMenu(event);}, items : [ { + id: 'addAttribute', label : self.editor.options.i18n[self.editor.options.userLang].addAttribute, enabled : true, show: true, @@ -2978,6 +2978,7 @@ function MenuBar(editor) { self.editor.addNode(selected, "attribute", false); } }, { + id: 'addElement', label : self.editor.options.i18n[self.editor.options.userLang].addElement, enabled : true, show: true, @@ -2989,6 +2990,7 @@ function MenuBar(editor) { } } }, { + id: 'addChildElement', label : self.editor.options.i18n[self.editor.options.userLang].addChildElement, enabled : true, show: true, @@ -2999,6 +3001,7 @@ function MenuBar(editor) { self.editor.addNode(selected, "element", false); } }, { + id: 'addSiblingElement', label : self.editor.options.i18n[self.editor.options.userLang].addSiblingElement, enabled : true, show: true, @@ -3009,6 +3012,7 @@ function MenuBar(editor) { self.editor.addNode(selected.parentElement, "element", false, selected); } }, { + id: 'addElementToParent', label : self.editor.options.i18n[self.editor.options.userLang].addElementToParent, enabled : true, show: true, @@ -3019,6 +3023,7 @@ function MenuBar(editor) { self.editor.addNode(selected.parentElement, "element", false); } }, { + id: 'addElementToRoot', label : self.editor.options.i18n[self.editor.options.userLang].addElementToRoot, enabled : true, show: true, @@ -3027,6 +3032,7 @@ function MenuBar(editor) { self.editor.addNode(self.editor.guiEditor.rootElement, "element", false); } }, { + id: 'addTextToElement', label : self.editor.options.i18n[self.editor.options.userLang].addTextToElement, enabled : true, show: true, @@ -3037,6 +3043,7 @@ function MenuBar(editor) { self.editor.addNode(selected, "text", false); } }, { + id: 'addCommentToElement', label : self.editor.options.i18n[self.editor.options.userLang].addCommentToElement, enabled : true, show: true, @@ -3047,6 +3054,7 @@ function MenuBar(editor) { self.editor.addNode(selected, "comment", false); } }, { + id: 'addCDataToElement', label : self.editor.options.i18n[self.editor.options.userLang].addCDataToElement, enabled : true, show: true, @@ -3058,11 +3066,13 @@ function MenuBar(editor) { } } ] }, { + id: 'view', label : self.editor.options.i18n[self.editor.options.userLang].view, enabled : true, show: true, action : function(event) {self.activateMenu(event);}, items : [ { + id: 'switchToXml', label : self.editor.options.i18n[self.editor.options.userLang].switchToXml, enabled : true, binding : self.editor.options.i18n[self.editor.options.userLang].altShiftX, @@ -3070,6 +3080,7 @@ function MenuBar(editor) { self.editor.modeChange(0); } }, { + id: 'switchToText', label : self.editor.options.i18n[self.editor.options.userLang].switchToText, enabled : true, binding : self.editor.options.i18n[self.editor.options.userLang].altShiftT, @@ -3078,11 +3089,13 @@ function MenuBar(editor) { } } ] }, { + id: 'options', label : self.editor.options.i18n[self.editor.options.userLang].options, enabled : true, show: true, action : function(event) {self.activateMenu(event);}, items : [ { + id: 'prettifyXml', label : self.editor.options.i18n[self.editor.options.userLang].prettifyXml, enabled : true, show: true, @@ -3092,6 +3105,7 @@ function MenuBar(editor) { self.checkEntry(this, self.editor.options.prettyXML); } }, { + id: 'enableShortcuts', label : self.editor.options.i18n[self.editor.options.userLang].enableShortcuts, enabled : true, show: true, @@ -3101,6 +3115,7 @@ function MenuBar(editor) { self.checkEntry(this, self.editor.options.enableGUIKeybindings); } }, { + id: 'enforeMinMaxOccurs', label : self.editor.options.i18n[self.editor.options.userLang].enforceMinMaxOccurs, enabled : self.editor.options.enforceOccurs, show: true, @@ -3111,6 +3126,7 @@ function MenuBar(editor) { self.checkEntry(this, self.editor.options.enforceOccurs); } }, { + id: 'prependNewElements', label : self.editor.options.i18n[self.editor.options.userLang].prependNewElements, enabled : true, show: true, @@ -3121,16 +3137,19 @@ function MenuBar(editor) { } } ] }/*, { + id: 'help', label : 'Help', enabled : true, action : function(event) {self.activateMenu(event);}, items : [ { + id: 'modsOutline', label : 'MODS Outline of Elements', enabled : true, binding : null, action : "http://www.loc.gov/standards/mods/mods-outline.html" } ] }*/, { + id: 'xml', label : self.editor.options.i18n[self.editor.options.userLang].xml, enabled : true, show: true, @@ -3139,6 +3158,7 @@ function MenuBar(editor) { self.editor.modeChange(0); } }, { + id: 'text', label : self.editor.options.i18n[self.editor.options.userLang].text, enabled : true, show: true, @@ -3215,7 +3235,7 @@ MenuBar.prototype.generateMenuItem = function(menuItemData, parentMenu) { } var menuBar = this; - menuItem.data("menuItemData", menuItemData).attr("id", xmlMenuHeaderPrefix + menuItemData.label.replace(/ /g, "_")); + menuItem.data("menuItemData", menuItemData).attr("id", xmlMenuHeaderPrefix + menuItemData.id); if (menuItemData.items !== undefined && menuItemData.items.length > 0) { var subMenu = $("
              ").addClass('sub_menu').appendTo(menuItem); $.each(menuItemData.items, function() { @@ -3262,7 +3282,6 @@ MenuBar.prototype.checkEntry = function(menuItem, checked) { /** * Menu object for adding new elements to an existing element or document */ - function ModifyElementMenu(menuID, label, expanded, enabled, owner, editor, getRelativeToFunction) { this.menuID = menuID; this.label = label; @@ -3417,7 +3436,6 @@ ModifyElementMenu.prototype.populate = function(xmlElement) { /** * Menu panel for managing individual modification menus. */ - function ModifyMenuPanel(editor) { this.editor = editor; this.menus = {}; @@ -4054,7 +4072,6 @@ TextEditor.prototype.addAttributeEvent = function() { * Current implementation involves storing previous states of the XML document, * recorded each time a significant change occurs or the document is regenerated */ - function UndoHistory(xmlState, editor) { this.xmlState = xmlState; this.editor = editor; @@ -4134,7 +4151,6 @@ UndoHistory.prototype.captureSnapshot = function () { /** * Stores data representing a single attribute for an element */ - function XMLAttribute(objectType, xmlElement, editor) { AbstractXMLObject.call(this, objectType, editor); // the XMLElement object which this attribute belongs to. @@ -4170,7 +4186,8 @@ XMLAttribute.prototype.render = function (){ }).data('xmlAttribute', this).appendTo(this.xmlElement.getAttributeContainer()); var self = this; - if ("required" != this.objectType.use && this.editor.options.enableEdit) { // Don't add required attributes in readonly mode + var allowRemove = !this.editor.options.enforceRequired || "required" != this.objectType.use; + if (allowRemove && this.editor.options.enableEdit) { // Don't add required attributes in readonly mode var removeButton = document.createElement('a'); removeButton.appendChild(document.createTextNode('(x) ')); this.domNode[0].appendChild(removeButton); @@ -4505,7 +4522,6 @@ XMLCommentNode.prototype.isSelected = function() { * Stores data related to a single xml element as it is represented in both the base XML * document and GUI */ - function XMLElement(xmlNode, objectType, editor) { AbstractXMLObject.call(this, objectType, editor); // jquery object reference to the xml node represented by this object in the active xml document @@ -4728,13 +4744,15 @@ XMLElement.prototype.populateChildren = function() { } }); // Patch to add "required" attributes. Does require a secondary change to know the "use" attribute also. See xsd2json-use.js : SchemaProcessor.prototype.createDefinition - $.each(this.objectType.attributes, function(){ - if ("required" == this.use) { - var childElement = self.addAttribute(this); - // Needs a patch in addAttributeEvent, too -> non-provided addButton parameter must be handled. - self.editor.activeEditor.addAttributeEvent(self, childElement); - } - }); + if (self.editor.options.enforceRequired) { + $.each(this.objectType.attributes, function(){ + if ("required" == this.use) { + var childElement = self.addAttribute(this); + // Needs a patch in addAttributeEvent, too -> non-provided addButton parameter must be handled. + self.editor.activeEditor.addAttributeEvent(self, childElement); + } + }); + } }; XMLElement.prototype.initializeGUI = function () { @@ -5395,7 +5413,6 @@ XMLElementStub.prototype.focus = function() { * @param init_object * @constructor */ - function XMLTemplates(init_object) { this.template_path = init_object.options.templateOptions.templatePath; this.templates = init_object.options.templateOptions.templates; @@ -5761,7 +5778,7 @@ XMLUnspecifiedElement.prototype.addContentContainers = function (recursive) { var placeholder = document.createElement('div'); placeholder.className = 'placeholder'; - placeholder.appendChild(document.createTextNode(this.editor.options.i18n[this.options.userLang].useMenuToAddContents)); + placeholder.appendChild(document.createTextNode(this.editor.options.i18n[this.editor.options.userLang].useMenuToAddContents)); this.contentContainer.appendChild(placeholder); this.placeholder = $(placeholder); diff --git a/src/abstract_xml_object.js b/src/abstract_xml_object.js index 25c1f13..38fb8e5 100644 --- a/src/abstract_xml_object.js +++ b/src/abstract_xml_object.js @@ -56,8 +56,11 @@ AbstractXMLObject.prototype.createElementInput = function (inputID, startingValu // Set width of generated fields manually. if (this.objectType.attribute && startingValue) { var len = startingValue.length * 10; - if (len < 80) { len = 80; } - else if (len > 600) { len = 600; } + if (len < 80) { + len = 80; + } else if (len > 600) { + len = 600; + } if (len != -1) { input.style.width = len + "px"; } diff --git a/src/jquery.xmleditor.js b/src/jquery.xmleditor.js index b0a0557..761a060 100644 --- a/src/jquery.xmleditor.js +++ b/src/jquery.xmleditor.js @@ -74,30 +74,6 @@ var localName = function(node) { return node.nodeName.substring(index + 1); }; -// Extracts and returns URL parameters or the given default. -function getParam(name, def) { - name = name.replace(/[\[]/, "\\[").replace(/[\]]/, "\\]"); - var regex = new RegExp("[\\?&]" + name + "=([^&#]*)"), - results = regex.exec(location.search); - return results === null ? def : decodeURIComponent(results[1].replace(/\+/g, " ")); -} - -// Add method to String. -String.prototype.replaceAll = function(search, replace) { - //if replace is null, return original string otherwise it will - //replace search string with 'undefined'. - if(!replace) - return this; - return this.replace(new RegExp('[' + search + ']', 'g'), replace); -}; - -// Extract the user language first from the browsers default and let it overwrite via URL parameter "lang=". -var userLang = navigator.language || navigator.userLanguage; -userLang = getParam("lang", userLang); -if ("de" != userLang && "en" != userLang) { - userLang = "en"; -} - $.widget( "xml.xmlEditor", { options: { // Schema object to be used @@ -197,8 +173,8 @@ $.widget( "xml.xmlEditor", { noDocument:"Could not load specified document and no fallback provided, cannot start.", noStartingDocument:"No starting document.", failedToAddChild:"Failed to add child of type ", - undo:"ctrl+z", - redo:"ctrl+y", + ctrlZ:"ctrl+z", + ctrlY:"ctrl+y", del:"del", enter:"enter", elementUp:"alt+up", @@ -213,7 +189,7 @@ $.widget( "xml.xmlEditor", { shiftLeft:"shift+left", shiftRight:"shift+right", altA:"alt+a", - ltShiftE:"alt+shift+e", + altShiftE:"alt+shift+e", altShiftX:"alt+shift+x", altShiftS:"alt+shift+s", altShiftT:"alt+shift+t", @@ -322,8 +298,8 @@ $.widget( "xml.xmlEditor", { noDocument:"Das angegebene Dokument konnte nicht geladen werden und es ist kein Rückfalldokument definiert. Der Editor kann nicht starten.", noStartingDocument:"Es ist kein Startdokument definiert.", failedToAddChild:"Das Element des folgenden Typs konnte nicht hinzugefügt werden: ", - undo:"Strg+z", - redo:"Strg+y", + ctrlZ:"Strg+z", + ctrlY:"Strg+y", del:"Entf", enter:"Enter", elementUp:"Alt+oben", @@ -418,7 +394,9 @@ $.widget( "xml.xmlEditor", { } }, - userLang: getParam("lang", "en") + userLang: "en", + + enforceRequired: false }, @@ -610,8 +588,6 @@ $.widget( "xml.xmlEditor", { // Start loading the document for editing this.loadDocument(this.options.ajaxOptions, localXMLContent); - var editor = this.activeEditor; - setTimeout(function () { if (!!editor['selectNext']) { editor.selectNext(); } }, 200); }, // Load the schema object @@ -869,7 +845,7 @@ $.widget( "xml.xmlEditor", { }, addChildElement: function(parentElement, newElementDefinition, relativeTo, prepend) { - if (!parentElement.allowChildren) + if (!parentElement.allowChildren || !this.activeEditor.editor.options.enableEdit) return null; // If in the text editor view, synchronous the text to the xml model and ensure wellformedness @@ -944,6 +920,11 @@ $.widget( "xml.xmlEditor", { addAttribute: function(xmlElement, attrDefinition, instigator) { + // if we are in readonly mode, return without changing anything + if (!this.activeEditor.editor.options.enableEdit) { + return; + } + // Synchronize xml document if there are unsynchronized changes in the text editor if (this.xmlState.changesNotSynced()) { try { @@ -1007,6 +988,11 @@ $.widget( "xml.xmlEditor", { addNode: function(parentElement, nodeType, prepend, relativeTo) { + // if we are in readonly mode, return without changing anything + if (!this.activeEditor.editor.options.enableEdit) { + return; + } + // Synchronize xml document if there are unsynchronized changes in the text editor if (this.xmlState.changesNotSynced()) { try { @@ -1075,10 +1061,10 @@ $.widget( "xml.xmlEditor", { this.activeEditor = this.guiEditor; var editor = this.activeEditor; setTimeout(function() {editor.selectNext();}, 200); - $("#" + xmlMenuHeaderPrefix + this.options.i18n[this.options.userLang].xml.replace(/ /g, "_")).addClass("active_mode_tab"); + $("#" + xmlMenuHeaderPrefix + 'xml').addClass("active_mode_tab"); } else { this.activeEditor = this.textEditor; - $("#" + xmlMenuHeaderPrefix + this.options.i18n[this.options.userLang].text.replace(/ /g, "_")).addClass("active_mode_tab"); + $("#" + xmlMenuHeaderPrefix + 'text').addClass("active_mode_tab"); } this.activeEditor.activate(); if (this.ready) @@ -1504,31 +1490,22 @@ $.widget( "xml.xmlEditor", { // Menu Update functions refreshMenuUndo: function(self) { if (self.undoHistory.headIndex > 0) { - $("#" + xmlMenuHeaderPrefix + self.options.i18n[self.options.userLang].undoMenuitem.replaceAll(' ','_')).removeClass("disabled").data("menuItemData").enabled = true; + $("#" + xmlMenuHeaderPrefix + 'undoMenuitem').removeClass("disabled").data("menuItemData").enabled = true; } else { - $("#" + xmlMenuHeaderPrefix + self.options.i18n[self.options.userLang].undoMenuitem.replaceAll(' ','_')).addClass("disabled").data("menuItemData").enabled = false; + $("#" + xmlMenuHeaderPrefix + 'undoMenuitem').addClass("disabled").data("menuItemData").enabled = false; } if (self.undoHistory.headIndex < self.undoHistory.states.length - 1) { - $("#" + xmlMenuHeaderPrefix + self.options.i18n[self.options.userLang].redoMenuitem.replaceAll(' ','_')).removeClass("disabled").data("menuItemData").enabled = true; + $("#" + xmlMenuHeaderPrefix + 'redoMenuitem').removeClass("disabled").data("menuItemData").enabled = true; } else { - $("#" + xmlMenuHeaderPrefix + self.options.i18n[self.options.userLang].redoMenuitem.replaceAll(' ','_')).addClass("disabled").data("menuItemData").enabled = false; + $("#" + xmlMenuHeaderPrefix + 'redoMenuitem').addClass("disabled").data("menuItemData").enabled = false; } }, // Performs updates to the menu for changing element/attribute selection refreshMenuSelected: function(self) { - var suffixes = [self.options.i18n[self.options.userLang].deselect.replaceAll(' ','_'), - self.options.i18n[self.options.userLang].nextElement.replaceAll(' ','_'), - self.options.i18n[self.options.userLang].previousElement.replaceAll(' ','_'), - self.options.i18n[self.options.userLang].parentElement.replaceAll(' ','_'), - self.options.i18n[self.options.userLang].firstChild.replaceAll(' ','_'), - self.options.i18n[self.options.userLang].nextSibling.replaceAll(' ','_'), - self.options.i18n[self.options.userLang].previousSibling.replaceAll(' ','_'), - self.options.i18n[self.options.userLang].nextAttribute.replaceAll(' ','_'), - self.options.i18n[self.options.userLang].previousAttribute.replaceAll(' ','_'), - self.options.i18n[self.options.userLang].deleteElement.replaceAll(' ','_'), - self.options.i18n[self.options.userLang].moveElementUp.replaceAll(' ','_'), - self.options.i18n[self.options.userLang].moveElementDown.replaceAll(' ','_')]; + var suffixes = ['deselect', 'nextElement', 'previousElement', 'parentElement', 'firstChild', + 'nextSibling', 'previousSibling', 'nextAttribute', 'previousAttribute', 'deleteElement', + 'moveElementUp', 'moveElementDown']; var hasSelected = self.guiEditor.selectedElement != null && self.guiEditor.active; $.each(suffixes, function(){ if (hasSelected) diff --git a/src/menu_bar.js b/src/menu_bar.js index bea1ec9..ae271f0 100644 --- a/src/menu_bar.js +++ b/src/menu_bar.js @@ -27,6 +27,7 @@ function MenuBar(editor) { show: true, action : function(event) {self.activateMenu(event);}, items : [ { + id: 'submitChanges', label : self.editor.options.i18n[self.editor.options.userLang].submitChanges, enabled : defaultSubmitConfig != null, show: self.editor.options.enableEdit, @@ -35,6 +36,7 @@ function MenuBar(editor) { self.editor.uploadXML.call(self.editor, defaultSubmitConfig); } }, { + id: 'export', label : self.editor.options.i18n[self.editor.options.userLang].export, enabled : (typeof(Blob) !== undefined), show: true, @@ -42,27 +44,31 @@ function MenuBar(editor) { action : $.proxy(self.editor.exportXML, self.editor) } ] }, { + id: 'edit', label : self.editor.options.i18n[self.editor.options.userLang].edit, enabled : self.editor.options.enableEdit, // readonly mode show: self.editor.options.enableEdit, // readonly mode action : function(event) {self.activateMenu(event);}, items : [ { + id: 'undoMenuitem', label : self.editor.options.i18n[self.editor.options.userLang].undoMenuitem, enabled : true, show: true, - binding : self.editor.options.i18n[self.editor.options.userLang].undo, + binding : self.editor.options.i18n[self.editor.options.userLang].ctrlZ, action : function() { self.editor.undoHistory.changeHead(-1); } }, { + id: 'redoMenuitem', label : self.editor.options.i18n[self.editor.options.userLang].redoMenuitem, enabled : true, show: true, - binding : self.editor.options.i18n[self.editor.options.userLang].redo, + binding : self.editor.options.i18n[self.editor.options.userLang].ctrlY, action : function() { self.editor.undoHistory.changeHead(1); } }, { + id: 'deleteElement', label : self.editor.options.i18n[self.editor.options.userLang].deleteElement, enabled : true, show: true, @@ -71,6 +77,7 @@ function MenuBar(editor) { self.editor.guiEditor.deleteSelected(); } }, { + id: 'moveElementUp', label : self.editor.options.i18n[self.editor.options.userLang].moveElementUp, enabled : true, show: true, @@ -79,6 +86,7 @@ function MenuBar(editor) { self.editor.guiEditor.moveSelected(true); } }, { + id: 'moveElementDown', label : self.editor.options.i18n[self.editor.options.userLang].moveElementDown, enabled : true, show: true, @@ -88,11 +96,13 @@ function MenuBar(editor) { } } ] }, { + id: 'select', label : self.editor.options.i18n[self.editor.options.userLang].select, enabled : self.editor.options.enableEdit, // readonly mode show: self.editor.options.enableEdit, // readonly mode action : function(event) {self.activateMenu(event);}, items : [ { + id: 'deselect', label : self.editor.options.i18n[self.editor.options.userLang].deselect, enabled : true, show: true, @@ -101,6 +111,7 @@ function MenuBar(editor) { self.editor.guiEditor.deselect(); } },{ + id: 'nextElement', label : self.editor.options.i18n[self.editor.options.userLang].nextElement, enabled : true, show: true, @@ -109,6 +120,7 @@ function MenuBar(editor) { self.editor.guiEditor.selectNext(); } }, { + id: 'previousElement', label : self.editor.options.i18n[self.editor.options.userLang].previousElement, enabled : true, show: true, @@ -117,6 +129,7 @@ function MenuBar(editor) { self.editor.guiEditor.selectNext(true); } }, { + id: 'nextAttribute', label : self.editor.options.i18n[self.editor.options.userLang].nextAttribute, enabled : true, show: true, @@ -125,6 +138,7 @@ function MenuBar(editor) { self.editor.guiEditor.selectAttribute(); } }, { + id: 'previousAttribute', label : self.editor.options.i18n[self.editor.options.userLang].previousAttribute, enabled : true, show: true, @@ -133,6 +147,7 @@ function MenuBar(editor) { self.editor.guiEditor.selectAttribute(true); } }, { + id: 'parentElement', label : self.editor.options.i18n[self.editor.options.userLang].parentElement, enabled : true, show: true, @@ -141,6 +156,7 @@ function MenuBar(editor) { self.editor.guiEditor.selectParent(); } }, { + id: 'firstChild', label : self.editor.options.i18n[self.editor.options.userLang].firstChild, enabled : true, show: true, @@ -149,6 +165,7 @@ function MenuBar(editor) { self.editor.guiEditor.selectParent(true); } }, { + id: 'nextSibling', label : self.editor.options.i18n[self.editor.options.userLang].nextSibling, enabled : true, show: true, @@ -157,6 +174,7 @@ function MenuBar(editor) { self.editor.guiEditor.selectSibling(); } }, { + id: 'previousSibling', label : self.editor.options.i18n[self.editor.options.userLang].previousSibling, enabled : true, show: true, @@ -166,11 +184,13 @@ function MenuBar(editor) { } } ] }, { + id: 'insert', label : self.editor.options.i18n[self.editor.options.userLang].insert, enabled : true, show: self.editor.options.enableEdit, action : function(event) {self.activateMenu(event);}, items : [ { + id: 'addAttribute', label : self.editor.options.i18n[self.editor.options.userLang].addAttribute, enabled : true, show: true, @@ -181,6 +201,7 @@ function MenuBar(editor) { self.editor.addNode(selected, "attribute", false); } }, { + id: 'addElement', label : self.editor.options.i18n[self.editor.options.userLang].addElement, enabled : true, show: true, @@ -192,6 +213,7 @@ function MenuBar(editor) { } } }, { + id: 'addChildElement', label : self.editor.options.i18n[self.editor.options.userLang].addChildElement, enabled : true, show: true, @@ -202,6 +224,7 @@ function MenuBar(editor) { self.editor.addNode(selected, "element", false); } }, { + id: 'addSiblingElement', label : self.editor.options.i18n[self.editor.options.userLang].addSiblingElement, enabled : true, show: true, @@ -212,6 +235,7 @@ function MenuBar(editor) { self.editor.addNode(selected.parentElement, "element", false, selected); } }, { + id: 'addElementToParent', label : self.editor.options.i18n[self.editor.options.userLang].addElementToParent, enabled : true, show: true, @@ -222,6 +246,7 @@ function MenuBar(editor) { self.editor.addNode(selected.parentElement, "element", false); } }, { + id: 'addElementToRoot', label : self.editor.options.i18n[self.editor.options.userLang].addElementToRoot, enabled : true, show: true, @@ -230,6 +255,7 @@ function MenuBar(editor) { self.editor.addNode(self.editor.guiEditor.rootElement, "element", false); } }, { + id: 'addTextToElement', label : self.editor.options.i18n[self.editor.options.userLang].addTextToElement, enabled : true, show: true, @@ -240,6 +266,7 @@ function MenuBar(editor) { self.editor.addNode(selected, "text", false); } }, { + id: 'addCommentToElement', label : self.editor.options.i18n[self.editor.options.userLang].addCommentToElement, enabled : true, show: true, @@ -250,6 +277,7 @@ function MenuBar(editor) { self.editor.addNode(selected, "comment", false); } }, { + id: 'addCDataToElement', label : self.editor.options.i18n[self.editor.options.userLang].addCDataToElement, enabled : true, show: true, @@ -261,11 +289,13 @@ function MenuBar(editor) { } } ] }, { + id: 'view', label : self.editor.options.i18n[self.editor.options.userLang].view, enabled : true, show: true, action : function(event) {self.activateMenu(event);}, items : [ { + id: 'switchToXml', label : self.editor.options.i18n[self.editor.options.userLang].switchToXml, enabled : true, binding : self.editor.options.i18n[self.editor.options.userLang].altShiftX, @@ -273,6 +303,7 @@ function MenuBar(editor) { self.editor.modeChange(0); } }, { + id: 'switchToText', label : self.editor.options.i18n[self.editor.options.userLang].switchToText, enabled : true, binding : self.editor.options.i18n[self.editor.options.userLang].altShiftT, @@ -281,11 +312,13 @@ function MenuBar(editor) { } } ] }, { + id: 'options', label : self.editor.options.i18n[self.editor.options.userLang].options, enabled : true, show: true, action : function(event) {self.activateMenu(event);}, items : [ { + id: 'prettifyXml', label : self.editor.options.i18n[self.editor.options.userLang].prettifyXml, enabled : true, show: true, @@ -295,6 +328,7 @@ function MenuBar(editor) { self.checkEntry(this, self.editor.options.prettyXML); } }, { + id: 'enableShortcuts', label : self.editor.options.i18n[self.editor.options.userLang].enableShortcuts, enabled : true, show: true, @@ -304,6 +338,7 @@ function MenuBar(editor) { self.checkEntry(this, self.editor.options.enableGUIKeybindings); } }, { + id: 'enforeMinMaxOccurs', label : self.editor.options.i18n[self.editor.options.userLang].enforceMinMaxOccurs, enabled : self.editor.options.enforceOccurs, show: true, @@ -314,6 +349,7 @@ function MenuBar(editor) { self.checkEntry(this, self.editor.options.enforceOccurs); } }, { + id: 'prependNewElements', label : self.editor.options.i18n[self.editor.options.userLang].prependNewElements, enabled : true, show: true, @@ -324,16 +360,19 @@ function MenuBar(editor) { } } ] }/*, { + id: 'help', label : 'Help', enabled : true, action : function(event) {self.activateMenu(event);}, items : [ { + id: 'modsOutline', label : 'MODS Outline of Elements', enabled : true, binding : null, action : "http://www.loc.gov/standards/mods/mods-outline.html" } ] }*/, { + id: 'xml', label : self.editor.options.i18n[self.editor.options.userLang].xml, enabled : true, show: true, @@ -342,6 +381,7 @@ function MenuBar(editor) { self.editor.modeChange(0); } }, { + id: 'text', label : self.editor.options.i18n[self.editor.options.userLang].text, enabled : true, show: true, @@ -418,7 +458,7 @@ MenuBar.prototype.generateMenuItem = function(menuItemData, parentMenu) { } var menuBar = this; - menuItem.data("menuItemData", menuItemData).attr("id", xmlMenuHeaderPrefix + menuItemData.label.replace(/ /g, "_")); + menuItem.data("menuItemData", menuItemData).attr("id", xmlMenuHeaderPrefix + menuItemData.id); if (menuItemData.items !== undefined && menuItemData.items.length > 0) { var subMenu = $("
                ").addClass('sub_menu').appendTo(menuItem); $.each(menuItemData.items, function() { diff --git a/src/modify_element_menu.js b/src/modify_element_menu.js index 43ec44c..6697763 100644 --- a/src/modify_element_menu.js +++ b/src/modify_element_menu.js @@ -1,7 +1,6 @@ /** * Menu object for adding new elements to an existing element or document */ - function ModifyElementMenu(menuID, label, expanded, enabled, owner, editor, getRelativeToFunction) { this.menuID = menuID; this.label = label; diff --git a/src/modify_menu_panel.js b/src/modify_menu_panel.js index 609c7ea..04c9fc3 100644 --- a/src/modify_menu_panel.js +++ b/src/modify_menu_panel.js @@ -1,7 +1,6 @@ /** * Menu panel for managing individual modification menus. */ - function ModifyMenuPanel(editor) { this.editor = editor; this.menus = {}; diff --git a/src/undo_history.js b/src/undo_history.js index ead4405..304b484 100644 --- a/src/undo_history.js +++ b/src/undo_history.js @@ -4,7 +4,6 @@ * Current implementation involves storing previous states of the XML document, * recorded each time a significant change occurs or the document is regenerated */ - function UndoHistory(xmlState, editor) { this.xmlState = xmlState; this.editor = editor; diff --git a/src/xml_attribute.js b/src/xml_attribute.js index c7892d8..df9bc40 100644 --- a/src/xml_attribute.js +++ b/src/xml_attribute.js @@ -1,7 +1,6 @@ /** * Stores data representing a single attribute for an element */ - function XMLAttribute(objectType, xmlElement, editor) { AbstractXMLObject.call(this, objectType, editor); // the XMLElement object which this attribute belongs to. @@ -37,7 +36,8 @@ XMLAttribute.prototype.render = function (){ }).data('xmlAttribute', this).appendTo(this.xmlElement.getAttributeContainer()); var self = this; - if ("required" != this.objectType.use && this.editor.options.enableEdit) { // Don't add required attributes in readonly mode + var allowRemove = !this.editor.options.enforceRequired || "required" != this.objectType.use; + if (allowRemove && this.editor.options.enableEdit) { // Don't add required attributes in readonly mode var removeButton = document.createElement('a'); removeButton.appendChild(document.createTextNode('(x) ')); this.domNode[0].appendChild(removeButton); diff --git a/src/xml_element.js b/src/xml_element.js index 15cec26..a2175ea 100644 --- a/src/xml_element.js +++ b/src/xml_element.js @@ -2,7 +2,6 @@ * Stores data related to a single xml element as it is represented in both the base XML * document and GUI */ - function XMLElement(xmlNode, objectType, editor) { AbstractXMLObject.call(this, objectType, editor); // jquery object reference to the xml node represented by this object in the active xml document @@ -225,13 +224,15 @@ XMLElement.prototype.populateChildren = function() { } }); // Patch to add "required" attributes. Does require a secondary change to know the "use" attribute also. See xsd2json-use.js : SchemaProcessor.prototype.createDefinition - $.each(this.objectType.attributes, function(){ - if ("required" == this.use) { - var childElement = self.addAttribute(this); - // Needs a patch in addAttributeEvent, too -> non-provided addButton parameter must be handled. - self.editor.activeEditor.addAttributeEvent(self, childElement); - } - }); + if (self.editor.options.enforceRequired) { + $.each(this.objectType.attributes, function(){ + if ("required" == this.use) { + var childElement = self.addAttribute(this); + // Needs a patch in addAttributeEvent, too -> non-provided addButton parameter must be handled. + self.editor.activeEditor.addAttributeEvent(self, childElement); + } + }); + } }; XMLElement.prototype.initializeGUI = function () { diff --git a/src/xml_templates.js b/src/xml_templates.js index 1272f75..19e26e4 100644 --- a/src/xml_templates.js +++ b/src/xml_templates.js @@ -3,7 +3,6 @@ * @param init_object * @constructor */ - function XMLTemplates(init_object) { this.template_path = init_object.options.templateOptions.templatePath; this.templates = init_object.options.templateOptions.templates; diff --git a/src/xml_unspecified_element.js b/src/xml_unspecified_element.js index c1d65ec..677db2c 100644 --- a/src/xml_unspecified_element.js +++ b/src/xml_unspecified_element.js @@ -88,7 +88,7 @@ XMLUnspecifiedElement.prototype.addContentContainers = function (recursive) { var placeholder = document.createElement('div'); placeholder.className = 'placeholder'; - placeholder.appendChild(document.createTextNode(this.editor.options.i18n[this.options.userLang].useMenuToAddContents)); + placeholder.appendChild(document.createTextNode(this.editor.options.i18n[this.editor.options.userLang].useMenuToAddContents)); this.contentContainer.appendChild(placeholder); this.placeholder = $(placeholder); diff --git a/xsd/src/schema_processor.js b/xsd/src/schema_processor.js index 3b2c9b9..497f8f2 100644 --- a/xsd/src/schema_processor.js +++ b/xsd/src/schema_processor.js @@ -80,7 +80,8 @@ SchemaProcessor.prototype.createDefinition = function(node, name) { values : [], type : null, ns: this.targetNSIndex, - np : true + np : true, + use : node.getAttribute("use") /* Patched for auto-add of required attributes. See also jquery.xmleditor.js : GUIEditor.prototype.addAttributeEvent and XMLElement.prototype.populateChildren */ }; if (name) diff --git a/xsd/xsd2json.js b/xsd/xsd2json.js index 7443068..85a5840 100644 --- a/xsd/xsd2json.js +++ b/xsd/xsd2json.js @@ -649,7 +649,8 @@ SchemaProcessor.prototype.createDefinition = function(node, name) { values : [], type : null, ns: this.targetNSIndex, - np : true + np : true, + use : node.getAttribute("use") /* Patched for auto-add of required attributes. See also jquery.xmleditor.js : GUIEditor.prototype.addAttributeEvent and XMLElement.prototype.populateChildren */ }; if (name) From bb6ce873878ffe56b7a086ecc71a3bc801801e54 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=B6schele?= Date: Fri, 2 Mar 2018 08:14:22 +0100 Subject: [PATCH 4/9] Fixed wrong path in requiredAttributes.html --- demo/requiredAttributes.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/demo/requiredAttributes.html b/demo/requiredAttributes.html index 10b93b4..3137971 100644 --- a/demo/requiredAttributes.html +++ b/demo/requiredAttributes.html @@ -28,14 +28,14 @@

                jQuery.xmleditor some sample XML with required attributes

            + + + + + + + + +
            +
            +

            jQuery.xmleditor Spoonful of Math MODS Editing Demo (Texteditor selected first)

            + View + the Project on GitHub UNC-Libraries/jquery.xmleditor + +
            +
            +
            +
            + + + + diff --git a/demo/readonly.html b/demo/readonly.html new file mode 100644 index 0000000..3e38d2e --- /dev/null +++ b/demo/readonly.html @@ -0,0 +1,48 @@ + + + + +jQuery.xmleditor MODS Document Editing Demo + + + + + + + + + + + + +
            +
            +

            jQuery.xmleditor Spoonful of Math MODS Editing Demo (Readonly Demo)

            + View + the Project on GitHub UNC-Libraries/jquery.xmleditor + +
            +
            +
            +
            + + + + From eeec1165ea687a28e29b290d6d30537373d3666f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=B6schele?= Date: Fri, 2 Mar 2018 08:38:07 +0100 Subject: [PATCH 6/9] Added demo for no source / XML switch in conjunction with editEnabled: false --- demo/mods-readonly-noswitch.html | 49 ++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) create mode 100644 demo/mods-readonly-noswitch.html diff --git a/demo/mods-readonly-noswitch.html b/demo/mods-readonly-noswitch.html new file mode 100644 index 0000000..819d6d1 --- /dev/null +++ b/demo/mods-readonly-noswitch.html @@ -0,0 +1,49 @@ + + + + +jQuery.xmleditor MODS Document Editing Demo + + + + + + + + + + + + +
            +
            +

            jQuery.xmleditor Spoonful of Math MODS Editing Demo (Texteditor selected first)

            + View + the Project on GitHub UNC-Libraries/jquery.xmleditor + +
            +
            +
            +
            + + + + From 3f152d450bdb4dbdf6a3dbed7a256eb45a802d07 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=B6schele?= Date: Fri, 2 Mar 2018 09:52:10 +0100 Subject: [PATCH 7/9] Fixed some problems of missing translations Fixed some build issues, as rake doesn't work for me --- demo/requiredAttribute/testmodel_example.xml | 33 +++++++++++++++++--- jquery.xmleditor.js | 6 ++-- package.json | 5 +-- src/jquery.xmleditor.js | 4 +-- xsd/xsd2json.js | 11 ++++--- 5 files changed, 42 insertions(+), 17 deletions(-) diff --git a/demo/requiredAttribute/testmodel_example.xml b/demo/requiredAttribute/testmodel_example.xml index ab5dbfd..50de1f1 100644 --- a/demo/requiredAttribute/testmodel_example.xml +++ b/demo/requiredAttribute/testmodel_example.xml @@ -1,6 +1,31 @@ - - - - Test + + + Value 1.1 + Value 1.2 + Value 1.3 + + Object 1.1 + Value 1.1.1 + Value 1.1.2 + Value 1.1.3 + + + + + Value 1.2.1 + Value 1.2.2 + Value 1.2.3 + + + + + + + + Value 2.1 + Value 2.2 + Value 2.3 + + \ No newline at end of file diff --git a/jquery.xmleditor.js b/jquery.xmleditor.js index fb15ccd..e432a42 100644 --- a/jquery.xmleditor.js +++ b/jquery.xmleditor.js @@ -1,6 +1,4 @@ ;(function($){ - //= require_self -//= require_tree . /* @@ -498,7 +496,7 @@ $.widget( "xml.xmlEditor", { // Either an upload button or an export button this.submitButtonConfigs = [{ url : this.options.ajaxOptions.xmlUploadPath, - label : exporting? "Export" : "Submit changes", + label : exporting? this.options.i18n[this.options.userLang].export : this.options.i18n[this.options.userLang].submitChanges, onSubmit : exporting? this.exportXML : null, disabled : typeof(Blob) === undefined && exporting }]; @@ -877,7 +875,7 @@ $.widget( "xml.xmlEditor", { // No matching child definition and parent doesn't allow "any", so can't add child if (!objectType && !parentElement.objectType.any) { - return "Could not add child " + newElementDefinition + ", it is not a valid child of " + parentElement.objectType.localName; + return this.options.i18n[this.options.userLang].couldNotAdd1 + newElementDefinition + this.options.i18n[this.options.userLang].couldNotAdd2 + parentElement.objectType.localName; } } else { objectType = newElementDefinition; diff --git a/package.json b/package.json index c69d8da..2c5250c 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "jquery.xmleditor", "main": "jquery.xmleditor.js", - "version": "1.3.0", + "version": "1.3.1", "description": "A web browser based XML editor. It provides a general use graphical tool for creating new or modifying existing XML documents in your web browser. Information is extracted from an XML schema (XSD file) to provide the user with information about what elements, subelements and attributes are available at different points in the structure, and a GUI based means of adding or removing them from the document.", "keywords": [ "xml", @@ -21,7 +21,8 @@ "Ben Pennell (https://github.com/bbpennel)", "Mike Daines (https://github.com/mdaines)", "Dean Farrell ", - "Volker Diels-Grabsch (https://github.com/m-click)" + "Volker Diels-Grabsch (https://github.com/m-click)", + "Marc Höschele (https://github.com/mhoesche)" ], "license": "Apache 2.0" } \ No newline at end of file diff --git a/src/jquery.xmleditor.js b/src/jquery.xmleditor.js index 761a060..c1dcbc8 100644 --- a/src/jquery.xmleditor.js +++ b/src/jquery.xmleditor.js @@ -497,7 +497,7 @@ $.widget( "xml.xmlEditor", { // Either an upload button or an export button this.submitButtonConfigs = [{ url : this.options.ajaxOptions.xmlUploadPath, - label : exporting? "Export" : "Submit changes", + label : exporting? this.options.i18n[this.options.userLang].export : this.options.i18n[this.options.userLang].submitChanges, onSubmit : exporting? this.exportXML : null, disabled : typeof(Blob) === undefined && exporting }]; @@ -876,7 +876,7 @@ $.widget( "xml.xmlEditor", { // No matching child definition and parent doesn't allow "any", so can't add child if (!objectType && !parentElement.objectType.any) { - return "Could not add child " + newElementDefinition + ", it is not a valid child of " + parentElement.objectType.localName; + return this.options.i18n[this.options.userLang].couldNotAdd1 + newElementDefinition + this.options.i18n[this.options.userLang].couldNotAdd2 + parentElement.objectType.localName; } } else { objectType = newElementDefinition; diff --git a/xsd/xsd2json.js b/xsd/xsd2json.js index 85a5840..b39697d 100644 --- a/xsd/xsd2json.js +++ b/xsd/xsd2json.js @@ -1,5 +1,4 @@ -//= require_self -//= require_tree . +;var Xsd2Json = function() { /* Copyright 2008 The University of North Carolina at Chapel Hill @@ -103,7 +102,8 @@ Xsd2Json.prototype.exportJSON = function(filename, variableName, pretty) { , false, false, false, false, 0, null ); a.dispatchEvent(event); -};/** +}; +/** * Manages processing of a set of schemas to produce a single resulting * tree of elements * @@ -567,7 +567,8 @@ SchemaManager.prototype.getNamespaceIndex = function(namespaceUri) { SchemaManager.prototype.getNamespaceUri = function(index) { return this.namespaceIndexes[index]; -};/** +}; +/** * Processes an XML schema, extracting key features and storing them into * javascript structures. * @@ -1148,4 +1149,4 @@ SchemaProcessor.prototype.getLocalNamespacePrefix = function(namespaceUri) { return prefix; } return null; -}; \ No newline at end of file +};; return Xsd2Json;}.call(); From 2b09404e5144320204441f5d16c4826bde37ccfb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=B6schele?= Date: Fri, 2 Mar 2018 10:22:09 +0100 Subject: [PATCH 8/9] Fixed output of combined .js files --- jquery.xmleditor.js | 4 ++-- xsd/xsd2json.js | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/jquery.xmleditor.js b/jquery.xmleditor.js index e432a42..a35c22d 100644 --- a/jquery.xmleditor.js +++ b/jquery.xmleditor.js @@ -1,4 +1,4 @@ -;(function($){ +;(function($){ /* @@ -5793,4 +5793,4 @@ XMLUnspecifiedElement.prototype.updateChildrenCount = function(childElement, del XMLUnspecifiedElement.prototype.childCanBeRemoved = function(childType) { return true; }; -})(jQuery); +})(jQuery); diff --git a/xsd/xsd2json.js b/xsd/xsd2json.js index b39697d..3d8e5f7 100644 --- a/xsd/xsd2json.js +++ b/xsd/xsd2json.js @@ -1149,4 +1149,5 @@ SchemaProcessor.prototype.getLocalNamespacePrefix = function(namespaceUri) { return prefix; } return null; -};; return Xsd2Json;}.call(); +}; +; return Xsd2Json;}.call(); From ed299eb17fdfb4776213b6b94b0f58c044d17138 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=B6schele?= Date: Fri, 2 Mar 2018 10:34:32 +0100 Subject: [PATCH 9/9] Fixed title of demo to be in sync with what the demo shows --- demo/mods-readonly-noswitch.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/demo/mods-readonly-noswitch.html b/demo/mods-readonly-noswitch.html index 819d6d1..f67bec8 100644 --- a/demo/mods-readonly-noswitch.html +++ b/demo/mods-readonly-noswitch.html @@ -17,7 +17,7 @@
            -

            jQuery.xmleditor Spoonful of Math MODS Editing Demo (Texteditor selected first)

            +

            jQuery.xmleditor Spoonful of Math MODS Editing Demo (Showing non-editable XML view only)

            View the Project on GitHub UNC-Libraries/jquery.xmleditor