From 184ec228daa0c03342c9bc3b012173612bfa0e43 Mon Sep 17 00:00:00 2001 From: Mark Friedrich Date: Wed, 3 Oct 2018 19:39:06 +0200 Subject: [PATCH] - WIP Add texteditor for system description field, #698 --- js/app.js | 92 +- js/app/ui/module/system_info.js | 589 +++++++------ js/app/util.js | 66 +- js/lib/summernote/summernote.min.js | 3 + public/css/v1.4.2/pathfinder.css | 2 +- public/css/v1.4.2/pathfinder.css.map | 4 +- public/fonts/summernote.eot | Bin 0 -> 9082 bytes public/fonts/summernote.ttf | Bin 0 -> 8896 bytes public/fonts/summernote.woff | Bin 0 -> 15692 bytes public/js/v1.4.2/app.js | 92 +- public/js/v1.4.2/app/map/map.js | 2 +- public/js/v1.4.2/app/ui/module/system_info.js | 589 +++++++------ public/js/v1.4.2/app/util.js | 66 +- .../v1.4.2/lib/summernote/summernote.min.js | 3 + public/templates/modules/system_info.html | 9 +- sass/_colors.scss | 2 +- sass/layout/_all.scss | 3 +- sass/layout/_summernote.scss | 41 + sass/layout/_system-info.scss | 41 +- sass/library/summernote/_summernote.scss | 798 ++++++++++++++++++ sass/pathfinder.scss | 3 +- 21 files changed, 1702 insertions(+), 703 deletions(-) create mode 100644 js/lib/summernote/summernote.min.js create mode 100644 public/fonts/summernote.eot create mode 100644 public/fonts/summernote.ttf create mode 100644 public/fonts/summernote.woff create mode 100644 public/js/v1.4.2/lib/summernote/summernote.min.js create mode 100644 sass/layout/_summernote.scss create mode 100644 sass/library/summernote/_summernote.scss diff --git a/js/app.js b/js/app.js index 2b8dd65f..be1fe448 100644 --- a/js/app.js +++ b/js/app.js @@ -25,37 +25,38 @@ requirejs.config({ admin: './app/admin', // initial start "admin page" view notification: './app/notification', // "notification" view - jquery: 'lib/jquery-3.3.1.min', // v3.3.1 jQuery - bootstrap: 'lib/bootstrap.min', // v3.3.0 Bootstrap js code - http://getbootstrap.com/javascript - text: 'lib/requirejs/text', // v2.0.12 A RequireJS/AMD loader plugin for loading text resources. - mustache: 'lib/mustache.min', // v1.0.0 Javascript template engine - http://mustache.github.io - localForage: 'lib/localforage.min', // v1.4.2 localStorage library - https://mozilla.github.io/localForage - velocity: 'lib/velocity.min', // v1.5.1 animation engine - http://julian.com/research/velocity - velocityUI: 'lib/velocity.ui.min', // v5.2.0 plugin for velocity - http://julian.com/research/velocity/#uiPack - slidebars: 'lib/slidebars', // v0.10 Slidebars - side menu plugin http://plugins.adchsm.me/slidebars - jsPlumb: 'lib/dom.jsPlumb-1.7.6', // v1.7.6 jsPlumb (Vanilla)- main map draw plugin https://jsplumbtoolkit.com - farahey: 'lib/farahey-0.5', // v0.5 jsPlumb "magnetizing" extension - https://github.com/jsplumb/farahey - customScrollbar: 'lib/jquery.mCustomScrollbar.min', // v3.1.5 Custom scroll bars - http://manos.malihu.gr - mousewheel: 'lib/jquery.mousewheel.min', // v3.1.13 Mousewheel - https://github.com/jquery/jquery-mousewheel - xEditable: 'lib/bootstrap-editable.min', // v1.5.1 X-editable - in placed editing - morris: 'lib/morris.min', // v0.5.1 Morris.js - graphs and charts - raphael: 'lib/raphael-min', // v2.1.2 Raphaël - required for morris (dependency) - bootbox: 'lib/bootbox.min', // v4.4.0 Bootbox.js - custom dialogs - http://bootboxjs.com - easyPieChart: 'lib/jquery.easypiechart.min', // v2.1.6 Easy Pie Chart - HTML 5 pie charts - http://rendro.github.io/easy-pie-chart - peityInlineChart: 'lib/jquery.peity.min', // v3.2.1 Inline Chart - http://benpickles.github.io/peity/ - dragToSelect: 'lib/jquery.dragToSelect', // v1.1 Drag to Select - http://andreaslagerkvist.com/jquery/drag-to-select - hoverIntent: 'lib/jquery.hoverIntent.min', // v1.9.0 Hover intention - http://cherne.net/brian/resources/jquery.hoverIntent.html - fullScreen: 'lib/jquery.fullscreen.min', // v0.6.0 Full screen mode - https://github.com/private-face/jquery.fullscreen - select2: 'lib/select2.min', // v4.0.3 Drop Down customization - https://select2.github.io - validator: 'lib/validator.min', // v0.10.1 Validator for Bootstrap 3 - https://github.com/1000hz/bootstrap-validator - lazylinepainter: 'lib/jquery.lazylinepainter-1.5.1.min', // v1.5.1 SVG line animation plugin - http://lazylinepainter.info - blueImpGallery: 'lib/blueimp-gallery', // v2.21.3 Image Gallery - https://github.com/blueimp/Gallery - blueImpGalleryHelper: 'lib/blueimp-helper', // helper function for Blue Imp Gallery - blueImpGalleryBootstrap: 'lib/bootstrap-image-gallery', // v3.4.2 Bootstrap extension for Blue Imp Gallery - https://blueimp.github.io/Bootstrap-Image-Gallery - bootstrapConfirmation: 'lib/bootstrap-confirmation', // v1.0.5 Bootstrap extension for inline confirm dialog - https://github.com/tavicu/bs-confirmation - bootstrapToggle: 'lib/bootstrap-toggle.min', // v2.2.0 Bootstrap Toggle (Checkbox) - http://www.bootstraptoggle.com - lazyload: 'lib/jquery.lazyload.min', // v1.9.5 LazyLoader images - http://www.appelsiini.net/projects/lazyload - sortable: 'lib/sortable.min', // v1.6.0 Sortable - drag&drop reorder - https://github.com/rubaxa/Sortable + jquery: 'lib/jquery-3.3.1.min', // v3.3.1 jQuery + bootstrap: 'lib/bootstrap.min', // v3.3.0 Bootstrap js code - http://getbootstrap.com/javascript + text: 'lib/requirejs/text', // v2.0.12 A RequireJS/AMD loader plugin for loading text resources. + mustache: 'lib/mustache.min', // v1.0.0 Javascript template engine - http://mustache.github.io + localForage: 'lib/localforage.min', // v1.4.2 localStorage library - https://mozilla.github.io/localForage + velocity: 'lib/velocity.min', // v1.5.1 animation engine - http://julian.com/research/velocity + velocityUI: 'lib/velocity.ui.min', // v5.2.0 plugin for velocity - http://julian.com/research/velocity/#uiPack + slidebars: 'lib/slidebars', // v0.10 Slidebars - side menu plugin http://plugins.adchsm.me/slidebars + jsPlumb: 'lib/dom.jsPlumb-1.7.6', // v1.7.6 jsPlumb (Vanilla)- main map draw plugin https://jsplumbtoolkit.com + farahey: 'lib/farahey-0.5', // v0.5 jsPlumb "magnetizing" extension - https://github.com/jsplumb/farahey + customScrollbar: 'lib/jquery.mCustomScrollbar.min', // v3.1.5 Custom scroll bars - http://manos.malihu.gr + mousewheel: 'lib/jquery.mousewheel.min', // v3.1.13 Mousewheel - https://github.com/jquery/jquery-mousewheel + xEditable: 'lib/bootstrap-editable.min', // v1.5.1 X-editable - in placed editing + morris: 'lib/morris.min', // v0.5.1 Morris.js - graphs and charts + raphael: 'lib/raphael-min', // v2.1.2 Raphaël - required for morris (dependency) + bootbox: 'lib/bootbox.min', // v4.4.0 Bootbox.js - custom dialogs - http://bootboxjs.com + easyPieChart: 'lib/jquery.easypiechart.min', // v2.1.6 Easy Pie Chart - HTML 5 pie charts - http://rendro.github.io/easy-pie-chart + peityInlineChart: 'lib/jquery.peity.min', // v3.2.1 Inline Chart - http://benpickles.github.io/peity/ + dragToSelect: 'lib/jquery.dragToSelect', // v1.1 Drag to Select - http://andreaslagerkvist.com/jquery/drag-to-select + hoverIntent: 'lib/jquery.hoverIntent.min', // v1.9.0 Hover intention - http://cherne.net/brian/resources/jquery.hoverIntent.html + fullScreen: 'lib/jquery.fullscreen.min', // v0.6.0 Full screen mode - https://github.com/private-face/jquery.fullscreen + select2: 'lib/select2.min', // v4.0.3 Drop Down customization - https://select2.github.io + validator: 'lib/validator.min', // v0.10.1 Validator for Bootstrap 3 - https://github.com/1000hz/bootstrap-validator + lazylinepainter: 'lib/jquery.lazylinepainter-1.5.1.min', // v1.5.1 SVG line animation plugin - http://lazylinepainter.info + blueImpGallery: 'lib/blueimp-gallery', // v2.21.3 Image Gallery - https://github.com/blueimp/Gallery + blueImpGalleryHelper: 'lib/blueimp-helper', // helper function for Blue Imp Gallery + blueImpGalleryBootstrap: 'lib/bootstrap-image-gallery', // v3.4.2 Bootstrap extension for Blue Imp Gallery - https://blueimp.github.io/Bootstrap-Image-Gallery + bootstrapConfirmation: 'lib/bootstrap-confirmation', // v1.0.5 Bootstrap extension for inline confirm dialog - https://github.com/tavicu/bs-confirmation + bootstrapToggle: 'lib/bootstrap-toggle.min', // v2.2.0 Bootstrap Toggle (Checkbox) - http://www.bootstraptoggle.com + lazyload: 'lib/jquery.lazyload.min', // v1.9.5 LazyLoader images - http://www.appelsiini.net/projects/lazyload + sortable: 'lib/sortable.min', // v1.6.0 Sortable - drag&drop reorder - https://github.com/rubaxa/Sortable + summernote: 'lib/summernote/summernote.min', // v0.8.10 Summernote WYSIWYG editor // header animation easePack: 'lib/EasePack.min', @@ -135,44 +136,47 @@ requirejs.config({ } }, pnotify: { - deps : ['jquery'] + deps: ['jquery'] }, easyPieChart: { - deps : ['jquery'] + deps: ['jquery'] }, peityInlineChart: { - deps : ['jquery'] + deps: ['jquery'] }, dragToSelect: { - deps : ['jquery'] + deps: ['jquery'] }, hoverIntent: { - deps : ['jquery'] + deps: ['jquery'] }, fullScreen: { - deps : ['jquery'] + deps: ['jquery'] }, select2: { - deps : ['jquery', 'mousewheel'], + deps: ['jquery', 'mousewheel'], exports: 'Select2' }, validator: { - deps : ['jquery', 'bootstrap'] + deps: ['jquery', 'bootstrap'] }, lazylinepainter: { - deps : ['jquery', 'bootstrap'] + deps: ['jquery', 'bootstrap'] }, blueImpGallery: { - deps : ['jquery'] + deps: ['jquery'] }, bootstrapConfirmation: { - deps : ['bootstrap'] + deps: ['bootstrap'] }, bootstrapToggle: { - deps : ['jquery'] + deps: ['jquery'] }, lazyload: { - deps : ['jquery'] + deps: ['jquery'] + }, + summernote: { + deps: ['jquery'] } } }); diff --git a/js/app/ui/module/system_info.js b/js/app/ui/module/system_info.js index 0630e159..b19c7784 100644 --- a/js/app/ui/module/system_info.js +++ b/js/app/ui/module/system_info.js @@ -6,9 +6,9 @@ define([ 'jquery', 'app/init', 'app/util', - 'app/render', - 'app/map/util' -], ($, Init, Util, Render, MapUtil) => { + 'app/map/util', + 'summernote' +], ($, Init, Util, MapUtil) => { 'use strict'; let config = { @@ -40,49 +40,223 @@ define([ // description field descriptionArea: 'pf-system-info-description-area', // class for "description" area addDescriptionButtonClass: 'pf-system-info-description-button', // class for "add description" button - moduleElementToolbarClass: 'pf-table-tools', // class for "module toolbar" element - tableToolsActionClass: 'pf-table-tools-action', // class for "edit" action - descriptionTextareaElementClass: 'pf-system-info-description', // class for "description" textarea element (xEditable) - descriptionTextareaCharCounter: 'pf-form-field-char-count', // class for "character counter" element for form field // fonts - fontTriglivianClass: 'pf-triglivian' // class for "Triglivian" names (e.g. Abyssal systems) + fontTriglivianClass: 'pf-triglivian', // class for "Triglivian" names (e.g. Abyssal systems) + + // Summernote + defaultBgColor: '#e2ce48' }; - // disable Module update temporary (in case e.g. textarea is currently active) - let disableModuleUpdate = false; - - // animation speed values - let animationSpeedToolbarAction = 200; - // max character length for system description let maxDescriptionLength = 512; - /** - * shows the tool action element by animation - * @param toolsActionElement - */ - let showToolsActionElement = (toolsActionElement) => { - toolsActionElement.velocity('stop').velocity({ - opacity: 1, - height: '100%' - },{ - duration: animationSpeedToolbarAction, - display: 'block', - visibility: 'visible' - }); - }; - /** - * hides the tool action element by animation - * @param toolsActionElement - */ - let hideToolsActionElement = (toolsActionElement) => { - toolsActionElement.velocity('stop').velocity('reverse', { - display: 'none', - visibility: 'hidden' + let initTextEditor = (element, options) => { + + // "length" hint plugin --------------------------------------------------------------------------------------- + $.extend($.summernote.plugins, { + /** + * @param {Object} context - context object has status of editor. + */ + lengthField: function (context){ + let self = this; + let ui = $.summernote.ui; + + // add counter + context.memo('button.lengthField', () => { + return $('', { + class: ['text-right', 'txt-color'].join(' ') + }); + }); + + /** + * update counter element with left chars + * @param contents + */ + let updateCounter = (contents) => { + let maxTextLength = context.options.maxTextLength; + let textLength = contents.length; + let counter = context.layoutInfo.toolbar.find('kbd'); + let counterLeft = maxTextLength - textLength; + + counter.text(counterLeft).data('charCount', counterLeft); + counter.toggleClass('txt-color-red', maxTextLength <= textLength); + + // disable "save" button + let saveBtn = context.layoutInfo.toolbar.find('.btn-save'); + saveBtn.prop('disabled', maxTextLength < textLength); + }; + + // events + this.events = { + 'summernote.init': function (we, e) { + updateCounter(context.$note.summernote('code')); + }, + 'summernote.change': function(we, contents){ + updateCounter(contents); + + } + }; + } }); + + // "discard" button plugin ------------------------------------------------------------------------------------ + $.extend($.summernote.plugins, { + /** + * @param {Object} context - context object has status of editor. + */ + discardBtn: function (context){ + let self = this; + let ui = $.summernote.ui; + + // add button + context.memo('button.discardBtn', () => { + let button = ui.button({ + contents: '', + container: 'body', + click: function(){ + // show confirmation dialog + $(this).confirmation('show'); + } + }); + let $button = button.render(); + + // show "discard" changes confirmation + let confirmationSettings = { + container: 'body', + placement: 'top', + btnCancelClass: 'btn btn-sm btn-default', + btnCancelLabel: 'cancel', + btnCancelIcon: 'fas fa-fw fa-ban', + title: 'discard changes', + btnOkClass: 'btn btn-sm btn-warning', + btnOkLabel: 'discard', + btnOkIcon: 'fas fa-fw fa-times', + onConfirm: (e, target) => { + // discard all changes + context.$note.summernote('reset'); + context.$note.summernote('destroy'); + } + }; + $button.confirmation(confirmationSettings); + + return $button; + }); + } + }); + + // "save button ----------------------------------------------------------------------------------------------- + let saveBtn = context => { + let ui = $.summernote.ui; + let button = ui.button({ + contents: '', + container: 'body', + className: ['btn-success', 'btn-save'], + click: e => { + // save changes + if( !context.$note.summernote('isEmpty') ){ + // get current code + let description = context.$note.summernote('code'); + console.log('code to save: ', description) + } + + context.$note.summernote('destroy'); + } + }); + + return button.render(); + }; + + let defaultOptions = { + height: 68, // set editor height + minHeight: 68, // set minimum height of editor + maxHeight: 500, // set maximum height of editor + dialogsInBody: true, + dialogsFade: true, + + //textareaAutoSync: false, + //hintDirection: 'right', + //tooltip: 'right', + //container: 'body', + + toolbar: [ + ['style', ['style']], + ['font', ['bold', 'underline', 'strikethrough', 'clear']], + ['color', ['color']], + //['para', ['ul', 'ol', 'paragraph']], + ['table', ['table']], + ['insert', ['link', 'hr']], + //['view', ['codeview', 'help']], + ['misc', ['undo', 'redo']], + ['lengthField'], + ['customBtn', [ 'discardBtn', 'saveBtn']] + ], + buttons: { + saveBtn: saveBtn + }, + insertTableMaxSize: { + col: 4, + row: 4 + }, + icons: { + //'align': 'note-icon-align', + 'alignCenter': 'fas fa-align-center', + 'alignJustify': 'fas fa-align-justify', + 'alignLeft': 'fas fa-align-left', + 'alignRight': 'fas fa-align-right', + //'rowBelow': 'note-icon-row-below', + //'colBefore': 'note-icon-col-before', + //'colAfter': 'note-icon-col-after', + //'rowAbove': 'note-icon-row-above', + //'rowRemove': 'note-icon-row-remove', + //'colRemove': 'note-icon-col-remove', + 'indent': 'fas fa-indent', + 'outdent': 'fas fa-outdent', + 'arrowsAlt': 'fas fa-expand-arrows-alt', + 'bold': 'fas fa-bold', + 'caret': 'fas fa-caret-down', + 'circle': 'fas fa-circle', + 'close': 'fas fa-time', + 'code': 'fas fa-code', + 'eraser': 'fas fa-eraser', + 'font': 'fas fa-font', + //'frame': 'note-icon-frame', + 'italic': 'fas fa-italic', + 'link': 'fas fa-link', + 'unlink': 'fas fa-unlink', + 'magic': 'fas fa-magic', + 'menuCheck': 'fas fa-check', + 'minus': 'fas fa-minus', + 'orderedlist': 'fas fa-list-ol', + 'pencil': 'fa-pen', + 'picture': 'fas fa-image', + 'question': 'fas fa-question', + 'redo': 'fas fa-redo', + 'square': 'fas fa-square', + 'strikethrough': 'fas fa-strikethrough', + 'subscript': 'fas fa-subscript', + 'superscript': 'fas fa-superscript', + 'table': 'fas fa-table', + 'textHeight': 'fas fa-text-height', + 'trash': 'fas fa-trash', + 'underline': 'fas fa-underline', + 'undo': 'fas fa-undo', + 'unorderedlist': 'fas fa-list-ul', + 'video': 'fab fa-youtube' + }, + colors: [ + ['#5cb85c', '#e28a0d', '#d9534f', '#e06fdf', '#9fa8da', '#e2ce48', '#428bca'] + ], + colorsName: [ + ['Green', 'Orange', 'Red', 'Pink', 'Indigo', 'Yellow', 'Blue'] + ], + }; + + options = $.extend({}, defaultOptions, options); + + element.summernote(options); }; /** @@ -112,42 +286,19 @@ define([ // update description textarea ---------------------------------------------------------------------------- let descriptionTextareaElement = moduleElement.find('.' + config.descriptionTextareaElementClass); - let description = descriptionTextareaElement.editable('getValue', true); - - if( - !disableModuleUpdate && // don´t update if field is active - description !== systemData.description - ){ - // description changed - let descriptionButton = moduleElement.find('.' + config.addDescriptionButtonClass); - - // set new value - descriptionTextareaElement.editable('setValue', systemData.description); - - let actionElement = descriptionButton.siblings('.' + config.tableToolsActionClass); - - if(systemData.description.length === 0){ - // show/activate description field - // show button if value is empty - descriptionButton.show(); - hideToolsActionElement(actionElement); - }else{ - // hide/disable description field - // hide tool button - descriptionButton.hide(); - showToolsActionElement(actionElement); + if(typeof descriptionTextareaElement.data().summernote === 'object'){ + // "Summernote" editor is currently open + console.log('Open'); + }else{ + // not open + console.log('NOT open'); + let description = descriptionTextareaElement.html(); + console.log(description); + console.log('update: ', description === systemData.description); + if(description !== systemData.description){ + descriptionTextareaElement.html(systemData.description); } } - - // created/updated tooltip -------------------------------------------------------------------------------- - let nameRowElement = moduleElement.find('.' + config.systemInfoNameClass); - - let tooltipData = { - created: systemData.created, - updated: systemData.updated - }; - - nameRowElement.addCharacterInfoTooltip( tooltipData ); } moduleElement.find('.' + config.descriptionArea).hideLoadingAnimation(); @@ -181,188 +332,7 @@ define([ let effectName = MapUtil.getEffectInfoForSystem(systemData.effect, 'name'); let effectClass = MapUtil.getEffectInfoForSystem(systemData.effect, 'class'); - // systemInfo template config - let moduleConfig = { - name: 'modules/system_info', - position: moduleElement, - link: 'append', - functions: { - after: function(conf){ - let tempModuleElement = conf.position; - // lock "description" field until first update - tempModuleElement.find('.' + config.descriptionArea).showLoadingAnimation(); - // "add description" button - let descriptionButton = tempModuleElement.find('.' + config.addDescriptionButtonClass); - // description textarea element - let descriptionTextareaElement = tempModuleElement.find('.' + config.descriptionTextareaElementClass); - - // init description textarea - descriptionTextareaElement.editable({ - url: Init.path.saveSystem, - dataType: 'json', - pk: systemData.id, - type: 'textarea', - mode: 'inline', - emptytext: '', - onblur: 'cancel', - showbuttons: true, - value: '', // value is set by trigger function updateModule() - rows: 5, - name: 'description', - inputclass: config.descriptionTextareaElementClass, - tpl: '', - params: function(params){ - params.mapData = { - id: mapId - }; - - params.systemData = {}; - params.systemData.id = params.pk; - params.systemData[params.name] = params.value; - - // clear unnecessary data - delete params.pk; - delete params.name; - delete params.value; - - return params; - }, - validate: function(value){ - if(value.length > 0 && $.trim(value).length === 0){ - return {newValue: ''}; - } - }, - success: function(response, newValue){ - Util.showNotify({title: 'System updated', text: 'Name: ' + response.name, type: 'success'}); - }, - error: function(jqXHR, newValue){ - let reason = ''; - let status = ''; - if(jqXHR.name){ - // save error new sig (mass save) - reason = jqXHR.name; - status = 'Error'; - }else{ - reason = jqXHR.responseJSON.text; - status = jqXHR.status; - } - - Util.showNotify({title: status + ': save system information', text: reason, type: 'warning'}); - $(document).setProgramStatus('problem'); - return reason; - } - }); - - // on xEditable open ------------------------------------------------------------------------------ - descriptionTextareaElement.on('shown', function(e, editable){ - let textarea = editable.input.$input; - - // disable module update until description field is open - disableModuleUpdate = true; - - // create character counter - let charCounter = $('', { - class: [config.descriptionTextareaCharCounter, 'txt-color', 'text-right'].join(' ') - }); - textarea.parent().next().append(charCounter); - - // update character counter - Util.updateCounter(textarea, charCounter, maxDescriptionLength); - - textarea.on('keyup', function(){ - Util.updateCounter($(this), charCounter, maxDescriptionLength); - }); - }); - - // on xEditable close ----------------------------------------------------------------------------- - descriptionTextareaElement.on('hidden', function(e){ - let value = $(this).editable('getValue', true); - if(value.length === 0){ - // show button if value is empty - hideToolsActionElement(descriptionButton.siblings('.' + config.tableToolsActionClass)); - descriptionButton.show(); - } - - // enable module update - disableModuleUpdate = false; - }); - - // enable xEditable field on Button click --------------------------------------------------------- - descriptionButton.on('click', function(e){ - e.stopPropagation(); - let descriptionButton = $(this); - - // hide tool buttons - descriptionButton.hide(); - - // show field *before* showing the element - descriptionTextareaElement.editable('show'); - - showToolsActionElement(descriptionButton.siblings('.' + config.tableToolsActionClass)); - }); - - // init tooltips ---------------------------------------------------------------------------------- - let tooltipElements = tempModuleElement.find('[data-toggle="tooltip"]'); - tooltipElements.tooltip({ - container: 'body', - placement: 'top' - }); - - // init system effect popover --------------------------------------------------------------------- - tempModuleElement.find('.' + config.systemInfoEffectClass).addSystemEffectTooltip(systemData.security, systemData.effect); - - // init planets popover --------------------------------------------------------------------------- - tempModuleElement.find('.' + config.systemInfoPlanetsClass).addSystemPlanetsTooltip(systemData.planets); - - // init static wormhole information --------------------------------------------------------------- - for(let staticData of staticsData){ - let staticRowElement = tempModuleElement.find('.' + config.systemInfoWormholeClass + staticData.name); - staticRowElement.addWormholeInfoTooltip(staticData); - } - - // copy system deeplink URL ----------------------------------------------------------------------- - tempModuleElement.find('.' + config.urlLinkClass).on('click', function(){ - let mapUrl = $(this).attr('data-url'); - Util.copyToClipboard(mapUrl).then(payload => { - if(payload.data){ - Util.showNotify({title: 'Copied to clipbaord', text: mapUrl, type: 'success'}); - } - }); - }); - - // constellation popover -------------------------------------------------------------------------- - tempModuleElement.find('a.popup-ajax').popover({ - html: true, - trigger: 'hover', - placement: 'top', - delay: 200, - container: 'body', - content: function(){ - return details_in_popup(this); - } - }); - - function details_in_popup(popoverElement){ - popoverElement = $(popoverElement); - let popover = popoverElement.data('bs.popover'); - - $.ajax({ - url: popoverElement.data('url'), - success: function(data){ - let systemEffectTable = Util.getSystemsInfoTable( data.systemsData ); - popover.options.content = systemEffectTable; - // reopen popover (new content size) - popover.show(); - } - }); - return 'Loading...'; - } - - } - } - }; - - let moduleData = { + let data = { system: systemData, static: staticsData, moduleHeadlineIconClass: config.moduleHeadlineIconClass, @@ -385,9 +355,7 @@ define([ trueSecClass: Util.getTrueSecClassForSystem( systemData.trueSec ), effectName: effectName, effectClass: effectClass, - moduleToolbarClass: config.moduleElementToolbarClass, descriptionButtonClass: config.addDescriptionButtonClass, - tableToolsActionClass: config.tableToolsActionClass, descriptionTextareaClass: config.descriptionTextareaElementClass, systemNameClass: () => { return (val, render) => { @@ -409,7 +377,109 @@ define([ systemUrlLinkClass: config.urlLinkClass }; - Render.showModule(moduleConfig, moduleData); + requirejs(['text!templates/modules/system_info.html', 'mustache'], (template, Mustache) => { + let content = Mustache.render(template, data); + moduleElement.append(content); + + // lock "description" field until first update + moduleElement.find('.' + config.descriptionArea).showLoadingAnimation(); + + // WYSIWYG init on button click --------------------------------------------------------------------------- + let descriptionButton = moduleElement.find('.' + config.addDescriptionButtonClass); + let descriptionTextareaElement = moduleElement.find('.' + config.descriptionTextareaElementClass); + + descriptionButton.on('click', function(e){ + e.stopPropagation(); + let descriptionButton = $(this); + // hide edit button + descriptionButton.hide(); + + initTextEditor(descriptionTextareaElement, { + focus: true, + placeholder: false, + maxTextLength: maxDescriptionLength, + disableDragAndDrop: true, + shortcuts: false, + callbacks: { + onInit: function(context){ + // set default background color + // -> could not figure out how to set by API as default color + context.toolbar.find('.note-current-color-button').attr('data-backcolor', config.defaultBgColor) + .find('.note-recent-color').css('background-color', config.defaultBgColor); + }, + onDestroy: function(context){ + descriptionButton.show(); + } + } + }); + }); + + // init system effect popover ----------------------------------------------------------------------------- + moduleElement.find('.' + config.systemInfoEffectClass).addSystemEffectTooltip(systemData.security, systemData.effect); + + // init planets popover ----------------------------------------------------------------------------------- + moduleElement.find('.' + config.systemInfoPlanetsClass).addSystemPlanetsTooltip(systemData.planets); + + // init static wormhole information ----------------------------------------------------------------------- + for(let staticData of staticsData){ + let staticRowElement = moduleElement.find('.' + config.systemInfoWormholeClass + staticData.name); + staticRowElement.addWormholeInfoTooltip(staticData); + } + + // copy system deeplink URL ------------------------------------------------------------------------------- + moduleElement.find('.' + config.urlLinkClass).on('click', function(){ + let mapUrl = $(this).attr('data-url'); + Util.copyToClipboard(mapUrl).then(payload => { + if(payload.data){ + Util.showNotify({title: 'Copied to clipbaord', text: mapUrl, type: 'success'}); + } + }); + }); + + // created/updated tooltip -------------------------------------------------------------------------------- + let nameRowElement = moduleElement.find('.' + config.systemInfoNameClass); + + let tooltipData = { + created: systemData.created, + updated: systemData.updated + }; + + nameRowElement.addCharacterInfoTooltip( tooltipData ); + + // constellation popover ---------------------------------------------------------------------------------- + moduleElement.find('a.popup-ajax').popover({ + html: true, + trigger: 'hover', + placement: 'top', + delay: 200, + container: 'body', + content: function(){ + return details_in_popup(this); + } + }); + + let details_in_popup = popoverElement => { + popoverElement = $(popoverElement); + let popover = popoverElement.data('bs.popover'); + + $.ajax({ + url: popoverElement.data('url'), + success: function(data){ + popover.options.content = Util.getSystemsInfoTable(data.systemsData); + // reopen popover (new content size) + popover.show(); + } + }); + return 'Loading...'; + }; + + // init tooltips ------------------------------------------------------------------------------------------ + let tooltipElements = moduleElement.find('[data-toggle="tooltip"]'); + tooltipElements.tooltip({ + container: 'body', + placement: 'top' + }); + }); return moduleElement; }; @@ -418,10 +488,8 @@ define([ * efore module destroy callback * @param moduleElement */ - let beforeDestroy = (moduleElement) => { - // remove xEditable description textarea - let descriptionTextareaElement = moduleElement.find('.' + config.descriptionTextareaElementClass); - descriptionTextareaElement.editable('destroy'); + let beforeDestroy = moduleElement => { + moduleElement.find('.' + config.descriptionTextareaElementClass).summernote('destroy'); moduleElement.destroyPopover(true); }; @@ -433,6 +501,3 @@ define([ beforeDestroy: beforeDestroy }; }); - - - diff --git a/js/app/util.js b/js/app/util.js index 682e2a7d..0d13e45f 100644 --- a/js/app/util.js +++ b/js/app/util.js @@ -500,6 +500,8 @@ define([ let data = {}; if( + tooltipData.created && + tooltipData.updated && tooltipData.created.character && tooltipData.updated.character ){ @@ -525,39 +527,41 @@ define([ createdStatusClass: statusCreatedClass, updatedStatusClass: statusUpdatedClass }; - } - let defaultOptions = { - placement: 'top', - html: true, - trigger: 'hover', - container: 'body', - title: 'Created / Updated', - delay: { - show: 150, - hide: 0 - } - }; - - options = $.extend({}, defaultOptions, options); - - return this.each(function(){ - let element = $(this); - - requirejs(['text!templates/tooltip/character_info.html', 'mustache'], (template, Mustache) => { - let content = Mustache.render(template, data); - - element.popover(options); - - // set new popover content - let popover = element.data('bs.popover'); - popover.options.content = content; - - if(options.show){ - element.popover('show'); + let defaultOptions = { + placement: 'top', + html: true, + trigger: 'hover', + container: 'body', + title: 'Created / Updated', + delay: { + show: 150, + hide: 0 } + }; + + options = $.extend({}, defaultOptions, options); + + return this.each(function(){ + let element = $(this); + + requirejs(['text!templates/tooltip/character_info.html', 'mustache'], (template, Mustache) => { + let content = Mustache.render(template, data); + + element.popover(options); + + // set new popover content + let popover = element.data('bs.popover'); + popover.options.content = content; + + if(options.show){ + element.popover('show'); + } + }); }); - }); + }else{ + return this; + } }; /** @@ -1777,7 +1781,7 @@ define([ /** * get a HTML table with system effect information * e.g. for popover - * @param data + * @param effects * @returns {string} */ let getSystemEffectTable = effects => { diff --git a/js/lib/summernote/summernote.min.js b/js/lib/summernote/summernote.min.js new file mode 100644 index 00000000..e41fc0f5 --- /dev/null +++ b/js/lib/summernote/summernote.min.js @@ -0,0 +1,3 @@ +/*! Summernote v0.8.10 | (c) 2013- Alan Hong and other contributors | MIT license */ + +!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?e(require("jquery")):"function"==typeof define&&define.amd?define(["jquery"],e):e(t.jQuery)}(this,function(t){"use strict";t=t&&t.hasOwnProperty("default")?t.default:t;var e=function(){function e(t,e,o,n){this.markup=t,this.children=e,this.options=o,this.callback=n}return e.prototype.render=function(e){var o=t(this.markup);if(this.options&&this.options.contents&&o.html(this.options.contents),this.options&&this.options.className&&o.addClass(this.options.className),this.options&&this.options.data&&t.each(this.options.data,function(t,e){o.attr("data-"+t,e)}),this.options&&this.options.click&&o.on("click",this.options.click),this.children){var n=o.find(".note-children-container");this.children.forEach(function(t){t.render(n.length?n:o)})}return this.callback&&this.callback(o,this.options),this.options&&this.options.callback&&this.options.callback(o),e&&e.append(o),o},e}(),o=function(o,n){return function(){var i="object"==typeof arguments[1]?arguments[1]:arguments[0],r=t.isArray(arguments[0])?arguments[0]:[];return i&&i.children&&(r=i.children),new e(o,r,i,n)}},n=o('
'),i=o('
'),r=o('
'),s=o('', - params: function(params){ - params.mapData = { - id: mapId - }; - - params.systemData = {}; - params.systemData.id = params.pk; - params.systemData[params.name] = params.value; - - // clear unnecessary data - delete params.pk; - delete params.name; - delete params.value; - - return params; - }, - validate: function(value){ - if(value.length > 0 && $.trim(value).length === 0){ - return {newValue: ''}; - } - }, - success: function(response, newValue){ - Util.showNotify({title: 'System updated', text: 'Name: ' + response.name, type: 'success'}); - }, - error: function(jqXHR, newValue){ - let reason = ''; - let status = ''; - if(jqXHR.name){ - // save error new sig (mass save) - reason = jqXHR.name; - status = 'Error'; - }else{ - reason = jqXHR.responseJSON.text; - status = jqXHR.status; - } - - Util.showNotify({title: status + ': save system information', text: reason, type: 'warning'}); - $(document).setProgramStatus('problem'); - return reason; - } - }); - - // on xEditable open ------------------------------------------------------------------------------ - descriptionTextareaElement.on('shown', function(e, editable){ - let textarea = editable.input.$input; - - // disable module update until description field is open - disableModuleUpdate = true; - - // create character counter - let charCounter = $('', { - class: [config.descriptionTextareaCharCounter, 'txt-color', 'text-right'].join(' ') - }); - textarea.parent().next().append(charCounter); - - // update character counter - Util.updateCounter(textarea, charCounter, maxDescriptionLength); - - textarea.on('keyup', function(){ - Util.updateCounter($(this), charCounter, maxDescriptionLength); - }); - }); - - // on xEditable close ----------------------------------------------------------------------------- - descriptionTextareaElement.on('hidden', function(e){ - let value = $(this).editable('getValue', true); - if(value.length === 0){ - // show button if value is empty - hideToolsActionElement(descriptionButton.siblings('.' + config.tableToolsActionClass)); - descriptionButton.show(); - } - - // enable module update - disableModuleUpdate = false; - }); - - // enable xEditable field on Button click --------------------------------------------------------- - descriptionButton.on('click', function(e){ - e.stopPropagation(); - let descriptionButton = $(this); - - // hide tool buttons - descriptionButton.hide(); - - // show field *before* showing the element - descriptionTextareaElement.editable('show'); - - showToolsActionElement(descriptionButton.siblings('.' + config.tableToolsActionClass)); - }); - - // init tooltips ---------------------------------------------------------------------------------- - let tooltipElements = tempModuleElement.find('[data-toggle="tooltip"]'); - tooltipElements.tooltip({ - container: 'body', - placement: 'top' - }); - - // init system effect popover --------------------------------------------------------------------- - tempModuleElement.find('.' + config.systemInfoEffectClass).addSystemEffectTooltip(systemData.security, systemData.effect); - - // init planets popover --------------------------------------------------------------------------- - tempModuleElement.find('.' + config.systemInfoPlanetsClass).addSystemPlanetsTooltip(systemData.planets); - - // init static wormhole information --------------------------------------------------------------- - for(let staticData of staticsData){ - let staticRowElement = tempModuleElement.find('.' + config.systemInfoWormholeClass + staticData.name); - staticRowElement.addWormholeInfoTooltip(staticData); - } - - // copy system deeplink URL ----------------------------------------------------------------------- - tempModuleElement.find('.' + config.urlLinkClass).on('click', function(){ - let mapUrl = $(this).attr('data-url'); - Util.copyToClipboard(mapUrl).then(payload => { - if(payload.data){ - Util.showNotify({title: 'Copied to clipbaord', text: mapUrl, type: 'success'}); - } - }); - }); - - // constellation popover -------------------------------------------------------------------------- - tempModuleElement.find('a.popup-ajax').popover({ - html: true, - trigger: 'hover', - placement: 'top', - delay: 200, - container: 'body', - content: function(){ - return details_in_popup(this); - } - }); - - function details_in_popup(popoverElement){ - popoverElement = $(popoverElement); - let popover = popoverElement.data('bs.popover'); - - $.ajax({ - url: popoverElement.data('url'), - success: function(data){ - let systemEffectTable = Util.getSystemsInfoTable( data.systemsData ); - popover.options.content = systemEffectTable; - // reopen popover (new content size) - popover.show(); - } - }); - return 'Loading...'; - } - - } - } - }; - - let moduleData = { + let data = { system: systemData, static: staticsData, moduleHeadlineIconClass: config.moduleHeadlineIconClass, @@ -385,9 +355,7 @@ define([ trueSecClass: Util.getTrueSecClassForSystem( systemData.trueSec ), effectName: effectName, effectClass: effectClass, - moduleToolbarClass: config.moduleElementToolbarClass, descriptionButtonClass: config.addDescriptionButtonClass, - tableToolsActionClass: config.tableToolsActionClass, descriptionTextareaClass: config.descriptionTextareaElementClass, systemNameClass: () => { return (val, render) => { @@ -409,7 +377,109 @@ define([ systemUrlLinkClass: config.urlLinkClass }; - Render.showModule(moduleConfig, moduleData); + requirejs(['text!templates/modules/system_info.html', 'mustache'], (template, Mustache) => { + let content = Mustache.render(template, data); + moduleElement.append(content); + + // lock "description" field until first update + moduleElement.find('.' + config.descriptionArea).showLoadingAnimation(); + + // WYSIWYG init on button click --------------------------------------------------------------------------- + let descriptionButton = moduleElement.find('.' + config.addDescriptionButtonClass); + let descriptionTextareaElement = moduleElement.find('.' + config.descriptionTextareaElementClass); + + descriptionButton.on('click', function(e){ + e.stopPropagation(); + let descriptionButton = $(this); + // hide edit button + descriptionButton.hide(); + + initTextEditor(descriptionTextareaElement, { + focus: true, + placeholder: false, + maxTextLength: maxDescriptionLength, + disableDragAndDrop: true, + shortcuts: false, + callbacks: { + onInit: function(context){ + // set default background color + // -> could not figure out how to set by API as default color + context.toolbar.find('.note-current-color-button').attr('data-backcolor', config.defaultBgColor) + .find('.note-recent-color').css('background-color', config.defaultBgColor); + }, + onDestroy: function(context){ + descriptionButton.show(); + } + } + }); + }); + + // init system effect popover ----------------------------------------------------------------------------- + moduleElement.find('.' + config.systemInfoEffectClass).addSystemEffectTooltip(systemData.security, systemData.effect); + + // init planets popover ----------------------------------------------------------------------------------- + moduleElement.find('.' + config.systemInfoPlanetsClass).addSystemPlanetsTooltip(systemData.planets); + + // init static wormhole information ----------------------------------------------------------------------- + for(let staticData of staticsData){ + let staticRowElement = moduleElement.find('.' + config.systemInfoWormholeClass + staticData.name); + staticRowElement.addWormholeInfoTooltip(staticData); + } + + // copy system deeplink URL ------------------------------------------------------------------------------- + moduleElement.find('.' + config.urlLinkClass).on('click', function(){ + let mapUrl = $(this).attr('data-url'); + Util.copyToClipboard(mapUrl).then(payload => { + if(payload.data){ + Util.showNotify({title: 'Copied to clipbaord', text: mapUrl, type: 'success'}); + } + }); + }); + + // created/updated tooltip -------------------------------------------------------------------------------- + let nameRowElement = moduleElement.find('.' + config.systemInfoNameClass); + + let tooltipData = { + created: systemData.created, + updated: systemData.updated + }; + + nameRowElement.addCharacterInfoTooltip( tooltipData ); + + // constellation popover ---------------------------------------------------------------------------------- + moduleElement.find('a.popup-ajax').popover({ + html: true, + trigger: 'hover', + placement: 'top', + delay: 200, + container: 'body', + content: function(){ + return details_in_popup(this); + } + }); + + let details_in_popup = popoverElement => { + popoverElement = $(popoverElement); + let popover = popoverElement.data('bs.popover'); + + $.ajax({ + url: popoverElement.data('url'), + success: function(data){ + popover.options.content = Util.getSystemsInfoTable(data.systemsData); + // reopen popover (new content size) + popover.show(); + } + }); + return 'Loading...'; + }; + + // init tooltips ------------------------------------------------------------------------------------------ + let tooltipElements = moduleElement.find('[data-toggle="tooltip"]'); + tooltipElements.tooltip({ + container: 'body', + placement: 'top' + }); + }); return moduleElement; }; @@ -418,10 +488,8 @@ define([ * efore module destroy callback * @param moduleElement */ - let beforeDestroy = (moduleElement) => { - // remove xEditable description textarea - let descriptionTextareaElement = moduleElement.find('.' + config.descriptionTextareaElementClass); - descriptionTextareaElement.editable('destroy'); + let beforeDestroy = moduleElement => { + moduleElement.find('.' + config.descriptionTextareaElementClass).summernote('destroy'); moduleElement.destroyPopover(true); }; @@ -433,6 +501,3 @@ define([ beforeDestroy: beforeDestroy }; }); - - - diff --git a/public/js/v1.4.2/app/util.js b/public/js/v1.4.2/app/util.js index 682e2a7d..0d13e45f 100644 --- a/public/js/v1.4.2/app/util.js +++ b/public/js/v1.4.2/app/util.js @@ -500,6 +500,8 @@ define([ let data = {}; if( + tooltipData.created && + tooltipData.updated && tooltipData.created.character && tooltipData.updated.character ){ @@ -525,39 +527,41 @@ define([ createdStatusClass: statusCreatedClass, updatedStatusClass: statusUpdatedClass }; - } - let defaultOptions = { - placement: 'top', - html: true, - trigger: 'hover', - container: 'body', - title: 'Created / Updated', - delay: { - show: 150, - hide: 0 - } - }; - - options = $.extend({}, defaultOptions, options); - - return this.each(function(){ - let element = $(this); - - requirejs(['text!templates/tooltip/character_info.html', 'mustache'], (template, Mustache) => { - let content = Mustache.render(template, data); - - element.popover(options); - - // set new popover content - let popover = element.data('bs.popover'); - popover.options.content = content; - - if(options.show){ - element.popover('show'); + let defaultOptions = { + placement: 'top', + html: true, + trigger: 'hover', + container: 'body', + title: 'Created / Updated', + delay: { + show: 150, + hide: 0 } + }; + + options = $.extend({}, defaultOptions, options); + + return this.each(function(){ + let element = $(this); + + requirejs(['text!templates/tooltip/character_info.html', 'mustache'], (template, Mustache) => { + let content = Mustache.render(template, data); + + element.popover(options); + + // set new popover content + let popover = element.data('bs.popover'); + popover.options.content = content; + + if(options.show){ + element.popover('show'); + } + }); }); - }); + }else{ + return this; + } }; /** @@ -1777,7 +1781,7 @@ define([ /** * get a HTML table with system effect information * e.g. for popover - * @param data + * @param effects * @returns {string} */ let getSystemEffectTable = effects => { diff --git a/public/js/v1.4.2/lib/summernote/summernote.min.js b/public/js/v1.4.2/lib/summernote/summernote.min.js new file mode 100644 index 00000000..e41fc0f5 --- /dev/null +++ b/public/js/v1.4.2/lib/summernote/summernote.min.js @@ -0,0 +1,3 @@ +/*! Summernote v0.8.10 | (c) 2013- Alan Hong and other contributors | MIT license */ + +!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?e(require("jquery")):"function"==typeof define&&define.amd?define(["jquery"],e):e(t.jQuery)}(this,function(t){"use strict";t=t&&t.hasOwnProperty("default")?t.default:t;var e=function(){function e(t,e,o,n){this.markup=t,this.children=e,this.options=o,this.callback=n}return e.prototype.render=function(e){var o=t(this.markup);if(this.options&&this.options.contents&&o.html(this.options.contents),this.options&&this.options.className&&o.addClass(this.options.className),this.options&&this.options.data&&t.each(this.options.data,function(t,e){o.attr("data-"+t,e)}),this.options&&this.options.click&&o.on("click",this.options.click),this.children){var n=o.find(".note-children-container");this.children.forEach(function(t){t.render(n.length?n:o)})}return this.callback&&this.callback(o,this.options),this.options&&this.options.callback&&this.options.callback(o),e&&e.append(o),o},e}(),o=function(o,n){return function(){var i="object"==typeof arguments[1]?arguments[1]:arguments[0],r=t.isArray(arguments[0])?arguments[0]:[];return i&&i.children&&(r=i.children),new e(o,r,i,n)}},n=o('
'),i=o('
'),r=o('
'),s=o('