NOTICE: By continued use of this site you understand and agree to the binding Terms of Service and Privacy Policy.
// ==UserScript== // @name xa_designer_overlay // @description Overlay with quality of life improvements // @version 0.99.992 // @require https://ajax.googleapis.com/ajax/libs/jquery/1.12.4/jquery.min.js // @require https://raw.githubusercontent.com/JDMcKinstry/jQuery.winFocus/master/jquery.winFocus.js // @require https://cdnjs.cloudflare.com/ajax/libs/chosen/1.8.7/chosen.jquery.min.js // @include /^https://.*-des\.f.v..d\.com// // @license MIT // @updateURL https://openuserjs.org/meta/ford.lambert/xa_designer_overlay.meta.js // @downloadURL https://openuserjs.org/install/ford.lambert/xa_designer_overlay.user.js // ==/UserScript== // ==OpenUserJS== // @author ford.lambert // ==/OpenUserJS== // jshint esversion: 6 // jshint jquery: true // globals $, jQuery (function(jQuery) { 'use strict'; jQuery.noConflict(); // do not use $ because it's also used by Prototype console.log(`%c Loading XA overlay`, 'color: #38C4CC') console.log(`%c Enable or disable XA overlay modules in it's menu on the top of the screen`, 'color: #38C4CC') let tabIsActive = true const modules = { regenMonitor: { active: getCookie('xa_regen_monitor').length ? ((getCookie('xa_regen_monitor') == 'true' || getCookie('xa_regen_monitor') == true) ? true : false) : true, cookie: 'xa_regen_monitor', label: 'Regen monitor', title: 'IceBlue Style required - Show production monitor infos with a colored logo on top left corner, green: done, yellow: running. Go inactive/active when tab loose/gain focus' }, shortcuts: { active: getCookie('xa_shortcuts').length ? ((getCookie('xa_shortcuts') == 'true' || getCookie('xa_shortcuts') == true) ? true : false) : true, cookie: 'xa_shortcuts', label: 'Keyboard shortcuts', title: 'Enable some keyboard shortcuts: ctrl + shift + f -> global fv search, ctrl + shift + s -> save and quit, ctrl + s -> save and continue' }, editorQol: { active: getCookie('xa_editor_qol').length ? ((getCookie('xa_editor_qol') == 'true' || getCookie('xa_editor_qol') == true) ? true : false) : true, cookie: 'xa_editor_qol', label: "Editor QOL", title: "Add some quality of life display when editing something, status, spec reference and level of sharing" }, generalLisibility: { active: getCookie('xa_lisibility').length ? ((getCookie('xa_lisibility') == 'true' || getCookie('xa_lisibility') == true) ? true : false) : true, cookie: 'xa_lisibility', label: "General lisibility", title: "Add some little things to boost lisibility: partial names are colored, always show lightened lightbulbs, show scss var colors" }, flash: { active: getCookie('xa_flash').length ? ((getCookie('xa_flash') == 'true' || getCookie('xa_flash') == true) ? true : false) : true, cookie: 'xa_flash', label: "Flash expire", title: "Remove flash messages after 5sec of display" }, fieldPresence: { active: getCookie('xa_field_presence').length ? ((getCookie('xa_field_presence') == 'true' || getCookie('xa_field_presence') == true) ? true : false) : true, cookie: 'xa_field_presence', label: "Field presence alert", title: "Display a warning if a model does not have a created_at and updated_at" }, userStory: { active: getCookie('xa_user_story').length ? ((getCookie('xa_user_story') == 'true' || getCookie('xa_user_story') == true) ? true : false) : false, cookie: 'xa_user_story', label: "User story warning", title: "Experimental, display a warning if you have no user story assigned" }, editorSize: { active: getCookie('xa_editor_size').length ? ((getCookie('xa_editor_size') == 'true' || getCookie('xa_editor_size') == true) ? true : false) : false, cookie: 'xa_editor_size', label: "Ace editor default size", title: "Save your last editor height and set it on load" }, regenButton: { active: getCookie('xa_regen_button').length ? ((getCookie('xa_regen_button') == 'true' || getCookie('xa_regen_button') == true) ? true : false) : true, cookie: 'xa_regen_button', label: "Add a regen button", title: "Add a regen button on top of models pages next to the Maveoc name, does exactly like the drag and drop of fields without the risk of missclick." }, dragDropLocker: { active: getCookie('xa_drag_drop_locker').length ? ((getCookie('xa_drag_drop_locker') == 'true' || getCookie('xa_drag_drop_locker') == true) ? true : false) : true, cookie: 'xa_drag_drop_locker', label: "Add a drag and drop locking button", title: "Add a button to 'lock' model and graphical views therefore avoiding missdrop. Locked by default. Also move the reminders away from this area to avoid their removing" }, qualityFramework: { active: getCookie('xa_quality_framework').length ? ((getCookie('xa_quality_framework') == 'true' || getCookie('xa_quality_framework') == true) ? true : false) : false, cookie: 'xa_quality_framework', label: "Experimental - Quality framework", title: "Experimental, add functionnalities to force the use of quality tools around the app (status / description...)" }, experimentalInterface: { active: getCookie('xa_experimental_interface').length ? ((getCookie('xa_experimental_interface') == 'true' || getCookie('xa_experimental_interface') == true) ? true : false) : false, cookie: 'xa_experimental_interface', label: "Experimental - Apply UI improvements", title: "The functionnality in this section will be merged elsewhere after user review. Currently: change certain select into chosens" }, } console.log({ modules }) const initOverlay = () => { setXaMenu() addKeybordShortcuts() limitFlashDuration() displayRequiredFieldNotice() displayUserStoryWarning() displayEditorInfos() displayVisibilityBoost() resizeEditor() addRegenButton() addDragDropLocker() initQualityFramework() initExperimentalUi() delay(1000).then(() => addRegenWatcher()) } // XA Menu ------> const setXaMenu = () => { const $xaMenuButton = jQuery('<button></button>') .attr('id', 'xa-menu-button') .text('XA - Menu') .css('margin', '0 20px') const $xaMenuBox = jQuery('<div></div>') .attr('id', 'xa-menu') .css('position', 'fixed') .css('top', '20vh') .css('left', '42vw') .css('padding', '10px') .css('box-shadow', '0px 0px 900px 50px black') .css('z-index', '9000') .css('background-color', 'white') .css('color', 'black') .css('visibility', 'hidden') .css('max-height', '60vh') .css('overflow', 'auto') const $xaMenu = jQuery('<form></form>') .attr('method', 'dialog') const $info = jQuery('<p></p>') .css('width', '350px') .css('font-size', '1.2em') .css('font-style', 'italic') .text('Changing the value a checkbox immediatly update a related cookie for enabling/disabling the module. Reload needed to apply effect (consider your background work before doing so...).') $xaMenuBox.append($info) for (const [key, module] of Object.entries(modules)) { const $xaMenuEntrie = jQuery('<div></div>') .css('display', 'flex') .css('justify-content', 'center') .css('align-items', 'center') .css('margin-bottom', '10px') .css('border-bottom', '1px solid grey') const $label = jQuery('<label></label>') .text(module.label) .css('width', '150px') .css('font-size', '1.2em') .attr('for', `xa-${module.cookie}-input`) const $input = jQuery('<input />') .attr('type', 'checkbox') .prop('checked', module.active) .css('margin', '5px 10px') .attr('id', `xa-${module.cookie}-input`) $input.on('change', function() { setLocalCookie(module.cookie, this.checked) }) const $info = jQuery('<img />') .attr('src', '/images/std/help.png?1199643545') .attr('alt', 'Help') .attr('title', module.title) $xaMenuEntrie.append($label) $xaMenuEntrie.append($input) $xaMenuEntrie.append($info) $xaMenu.append($xaMenuEntrie) $xaMenuBox.append($xaMenu); } const $menu = jQuery('<menu></menu>') .css('padding', '0') .css('display', 'flex') .css('justify-content', 'center') const $closeButton = jQuery('<button></button>') .attr('value', 'cancel') .text('Cancel') .css('margin', '10px') const $reloadButton = jQuery('<button></button>') .text('Reload to apply') .css('margin', '10px') .attr('onClick', 'location.reload()') $menu.append($closeButton) $menu.append($reloadButton) $xaMenu.append($menu) jQuery('#header-elts').append($xaMenuBox) jQuery('#header-elts').prepend($xaMenuButton) $xaMenuButton.on('click', () => { toggleModal(jQuery('#xa-menu')) }) $closeButton.on('click', () => { toggleModal(jQuery('#xa-menu')) }) } const toggleModal = ($modal) => { if($modal.css('visibility') == 'visible') { $modal.css('visibility', 'hidden') } else { $modal.css('visibility', 'visible') } } // XA Menu <------ // Regen watcher ------> const addRegenWatcher = () => { if(modules.regenMonitor.active && !window.location.href.includes('/tasks')) { const hasCustomStyle = jQuery('#title').find('h1').css('display') == 'none' displayLogo(hasCustomStyle) pollRegen() let monitor_pu = new Ajax.PeriodicalUpdater('production_monitor', '/changes/current_tasks', {method:'post', frequency:3, decay:2}) jQuery.winFocus({ // will only fire when current tab loses focus blur: () => { disableRegenWatcher(monitor_pu) tabIsActive = false }, // will only fire when current tab gains focus focus: () => { tabIsActive = true monitor_pu = new Ajax.PeriodicalUpdater('production_monitor', '/changes/current_tasks', {method:'post', frequency:3, decay:2}) pollRegen() } }); } } const disableRegenWatcher = (monitor_pu) => { monitor_pu.stop(); monitor_pu = undefined; } const pollRegen = () => { const inactiveLogo = "https://storage.gra.cloud.ovh.net/v1/AUTH_0b4eb0b702894162b8bcdc31088fb7dd/hexa-public/designer-script/default.png" const doneLogo = "https://storage.gra.cloud.ovh.net/v1/AUTH_0b4eb0b702894162b8bcdc31088fb7dd/hexa-public/designer-script/ready.png" const processingLogo = "https://storage.gra.cloud.ovh.net/v1/AUTH_0b4eb0b702894162b8bcdc31088fb7dd/hexa-public/designer-script/running.png" let text = '' jQuery('#production_monitor').find('td').each(function() { text += jQuery(this).text().trim() }) const isProcessing = text.length > 0 if(isProcessing) { changeLogo(processingLogo) } else { changeLogo(doneLogo) } setTimeout(function () { if(tabIsActive) { pollRegen() } else { changeLogo(inactiveLogo) } }, 2000) } const changeLogo = (logoPath) => { jQuery('#header').find('#title').css("background-image", "url(" + logoPath + ")") } const displayLogo = (hasCustomStyle) => { const logoPath = hasCustomStyle ? 'https://storage.gra.cloud.ovh.net/v1/AUTH_0b4eb0b702894162b8bcdc31088fb7dd/hexa-public/designer-script/default.png' : '/images/logo/logo.png' const $header = jQuery('#header') const $title = $header.find('#title') $title.css("background-image", "url(" + logoPath + ")") const $titleInfos = jQuery('<span></span>') .addClass('sub-title') .text($title.text()) $header.append($titleInfos) } // Regen watcher <------ // KEYBOARD SHORTCUTS ------> const addKeybordShortcuts = () => { if(modules.shortcuts.active) { jQuery(document).on('keydown', (e) => { const isMac = navigator.platform.match('Mac') const ctrlLikeKey = isMac ? e.metaKey : e.ctrlKey; const saveKeyCode = 83 const searchKeyCode = 70 if(isMac) { if (ctrlLikeKey && e.shiftKey && e.keyCode == saveKeyCode) { e.preventDefault(); // prevent "save html page" default event submitEditor() } } else { if (ctrlLikeKey && e.keyCode == saveKeyCode) { e.preventDefault(); // prevent "save html page" default event submitEditor() } } if (ctrlLikeKey && e.shiftKey && e.keyCode == searchKeyCode) { jQuery('#global_search_field').focus() } }) } } const submitEditor = () => { // const $saveContinue = jQuery('#new_create_form_submit').length ? jQuery('#new_create_form_submit') : jQuery('#continue_editing_form_submit'); // stayOnPage ? $saveContinue.click() : jQuery('#update_form_submit').click(); jQuery('#continue_editing_form_submit').click() } // <------ KEYBOARD SHORTCUTS // Info display ------> const limitFlashDuration = () => { if(modules.flash.active) { // After reload on save, a sticky flash stay on the top left, remove it after some time. setTimeout(function() { jQuery("#flash").hide() }, 5000) } } // <------ Info display // ------> Alert for basic fields presence (created_at, updated_at) // This script is brought to you by Aloïs, thanks to him and his amazing cat :) const displayRequiredFieldNotice = () => { if(modules.fieldPresence.active) { const created_at = document.querySelector("[title^='created_at']"); const updated_at = document.querySelector("[title^='updated_at']"); const sidebarNotes = document.querySelector("#field_types span"); const maveocName = document.querySelector("#content h2").innerText; const existingStorage = JSON.parse(JSON.parse(window.localStorage.getItem("disabled_notices"))); const disableButtons = document.querySelectorAll(".disable-notice"); if (created_at && updated_at) return; if (sidebarNotes) { if ((!created_at && existingStorage == null) || (!created_at && (existingStorage !== null && !existingStorage.includes(maveocName)))) { sidebarNotes.innerHTML += '<p class="date_fields_warning" style="color:red">You do not have a created_at field! <img alt="Del" class="disable-notice" style="cursor:pointer;" title="Disable ALL notice messages!" src="/images/faveod/del.png?1596181992"></p>'; }; if ((!updated_at && existingStorage == null) || (!updated_at && (existingStorage !== null && !existingStorage.includes(maveocName)))) { sidebarNotes.innerHTML += '<p class="date_fields_warning" style="color:red">You do not have an updated_at field! <img alt="Del" class="disable-notice" style="cursor:pointer;" title="Disable ALL notice messages!" src="/images/faveod/del.png?1596181992"></p>'; }; } disableButtons.forEach(button => button.addEventListener('click', removeNotices)); } } const removeRequiredFieldNotice = () => { const notices = document.querySelectorAll(".date_fields_warning"); notices.forEach(notice => notice.hide()); let value = ""; if (existingStorage !== null) { existingStorage.push(maveocName); value = JSON.stringify(existingStorage); } else { value = JSON.stringify([maveocName]); } window.localStorage.setItem(`disabled_notices`, value); } // <------ Alert for basic fields presence // ------> User Story warning const displayUserStoryWarning = () => { // Initially started this one to encourage always working with a user story // Enable or not depending of the project, disabled by default if(modules.userStory.active) { const $userStoryIndicator = jQuery('#working_on'); if(!$userStoryIndicator.length) { const $userStoryWarning = jQuery('<span></span>') .attr('id', 'user-story-warning') .text('WARNING ! You are currently working without any User story !') .css('color', '#D04158') .css('font-weight', 'bold') .css('font-size', '1.1em') const $warningLogo = jQuery('<span></span>') .attr('id', 'user-story-warning-logo') .css('background-image', 'url(https://i.imgur.com/JW2wKDs.png)') .css('height', '20px') .css('width', '20px') .css('display', 'inline-block') .css('background-position', 'center') .css('background-size', 'contain') .css('margin-bottom', '-4px') .css('margin-right', '4px') jQuery('#header-elts').prepend($userStoryWarning); $userStoryWarning.prepend($warningLogo); let counter = 1; const isOdd = (x) => { return x & 1 } setInterval(function() { if(isOdd(counter) == 1) { $warningLogo.css('background-image', 'url(https://i.imgur.com/JW2wKDs.png)') counter += 1; } else { $warningLogo.css('background-image', 'url(https://i.imgur.com/iqfdJGd.png)') counter += 1; } }, 1000); } } } // <------ User stroy warning // ------> Editor QOL const displayEditorInfos = () => { if(modules.editorQol.active) { displayStatus() displaySpecReference() displaySharing() } } const displayStatus = () => { const $originalStatus = jQuery("select[id$='_status']") if(!$originalStatus.length) { return } const $newStatus = $originalStatus.clone() $newStatus.attr('id', 'new-select') .val($originalStatus.val()) .css('margin-right', '10px') .css('border-radius', '5px') $newStatus.on('change', function() { $originalStatus.val(jQuery(this).val()) }) $originalStatus.on('change', function() { $newStatus.val(jQuery(this).val()) }) jQuery('#content h2.title').prepend($newStatus) } const displaySpecReference = () => { const $originalSpecRef = jQuery("input[id$='_spec_ref']") if(!$originalSpecRef.length) { return } const $newSpecRef = $originalSpecRef.clone() $newSpecRef.attr('id', 'new-specref') .attr('placeholder', "US Reference") .css('text-align', 'center') .css('border-radius', '5px') .css('margin-left', '10px') .css('display', 'inline-block') $newSpecRef.on('change', function() { $originalSpecRef.val(jQuery(this).val()) }) $originalSpecRef.on('change', function() { $newSpecRef.val(jQuery(this).val()) }) jQuery('#content h2.title').append($newSpecRef) } const displaySharing = () => { const currentAvailability = jQuery("select[id$='_shared_for']").val() const label = jQuery("select[id$='_shared_for'] option:selected").text() if(!jQuery("select[id$='_shared_for']").length) { return } const $wrapper = jQuery('<p></p>') const $availability = jQuery('<span></span>') .text(`Available for ${label}`) .css('border', '1px solid black') .css('background-color', 'grey') .css('color', 'white') .css('padding', '3px') .css('border-radius', '5px') .css('margin-left', '10px') if(currentAvailability == 3) { $wrapper.text('This code need manual regeneration to be applied') const $warningSign = jQuery('<i />') .addClass('muted glyphicon glyphicon-exclamation-sign') .css('color', 'orange') .css('margin-right', '5px') $wrapper.prepend($warningSign) } $wrapper.append($availability) jQuery('#content h2.title').append($wrapper) } // <------ Editor QOL // ------> General lisibility const displayVisibilityBoost = () => { if(modules.generalLisibility.active) { /* ----- Always show lightened lightbulbs ----- */ const wantedSrc = '/images/page_icons/lightbulb.png?1199643502'; jQuery('td').find(`img[src="${wantedSrc}"]`).parents('td').css('opacity', '1') /*------- Add a color if a view is a partial ------*/ jQuery('.partial').find('a').css('color', 'rgba(233, 96, 42, 0.7)') /* show color for scss variables */ const $variableTable = jQuery('#style_variables_table'); if($variableTable.length) { $variableTable.find('tbody').find('tr').each(function() { jQuery(this).find('td:nth-child(2)').css('background-color', jQuery(this).find('td:nth-child(4)').text()) }) } /* Add separator in search */ document.getElementById('global_search_field').addEventListener('input', () => { setTimeout(function(){ const separatorStyle = '1px dashed black' const resultBox = document.getElementById('gs_results') const entries = [] entries.push(resultBox.querySelector('.config_initializers')) entries.push(resultBox.querySelector('.cfg_dependencies')) entries.push(resultBox.querySelector('.use_cases')) entries.push(resultBox.querySelector('.style_variables')) entries.push(resultBox.querySelector('.javascripts')) entries.push(resultBox.querySelector('.js_frameworks')) entries.push(resultBox.querySelector('.js_functions')) entries.push(resultBox.querySelector('.styles')) entries.push(resultBox.querySelector('.layouts')) entries.push(resultBox.querySelector('.notifications')) entries.push(resultBox.querySelector('.aktions')) entries.push(resultBox.querySelector('.field_actions')) entries.push(resultBox.querySelector('.action_attribute_definitions')) entries.push(resultBox.querySelector('.field_formatters')) entries.push(resultBox.querySelector('.field_behaviours')) entries.push(resultBox.querySelector('.field_styles')) entries.push(resultBox.querySelector('.field_views')) entries.push(resultBox.querySelector('.view_helpers')) entries.push(resultBox.querySelector('.views')) entries.push(resultBox.querySelector('.model_tests')) entries.push(resultBox.querySelector('.model_logics')) entries.push(resultBox.querySelector('.fields')) entries.map((entry) => { if(entry != null) { entry.parentNode.style.borderTop = separatorStyle } }) }, 1000); }) } } // <------ General lisibility // ------> Editor Size const resizeEditor = () => { if(modules.editorSize.active) { let height = getCookie('text_editor_height').length ? getCookie('text_editor_height') : '454px' const $editor = jQuery('.ace_editor') $editor.css('height', height) window.addEventListener('beforeunload', function() { setLocalCookie('text_editor_height', $editor.css('height')) }) } } // <------ Editor Size // ------> Regen Button const addRegenButton = () => { // This script is brought to you by Thibault, thanks to him :) if(modules.regenButton.active && location.href.includes('/model/')) { const titles = document.querySelectorAll('h2.title') const title = titles[titles.length - 1] title.insertAdjacentHTML('beforeend', `<span id="regen">${String.fromCodePoint(0x1f504)}</span>`) const regen = document.querySelector('#regen') regen.style.cursor = 'pointer' let divs = document.querySelectorAll('fieldset div.moved_field_objects') let maveocId = document.querySelector('#content').data('in_use') let fields = [] divs.forEach((mfo, i )=> { if (divs[i + 1] == mfo.nextElementSibling && fields.length < 2) { fields.push(mfo.id.split('_')[1]) fields.push(divs[i + 1].id.split('_')[1]) } return fields }) regen.addEventListener('click', () => { if (!confirm("Do you want to regen this Maveoc ?")) return fetch("/fields/move_dropped", { "headers": { "accept": "text/javascript, text/html, application/xml, text/xml, */*", "accept-language": "en-GB,en;q=0.9,en-US;q=0.8,fr;q=0.7", "content-type": "application/x-www-form-urlencoded; charset=UTF-8", "sec-ch-ua": "\" Not A;Brand\";v=\"99\", \"Chromium\";v=\"96\", \"Google Chrome\";v=\"96\"", "sec-ch-ua-mobile": "?0", "sec-ch-ua-platform": "\"macOS\"", "sec-fetch-dest": "empty", "sec-fetch-mode": "cors", "sec-fetch-site": "same-origin", "x-csrf-token": `${document.querySelector('[name="csrf-token"]').content}`, "x-prototype-version": "1.6.1", "x-requested-with": "XMLHttpRequest" }, "referrerPolicy": "strict-origin-when-cross-origin", "body": `model=${maveocId}&receiver=${fields[0]}&id=${fields[1]}&_=`, "method": "POST", "mode": "cors", "credentials": "include" }).then(console.log(`%c Starting Maveoc regen...`, 'color: #38C4CC')) }) } } // <------ Regen Button // ------> DragDrop Locker const addDragDropLocker = () => { if(modules.dragDropLocker.active) { const addLocker = () => { return new Promise((resolve, reject) => { // add html button const titles = document.querySelectorAll('h2.title') const title = titles[titles.length - 1] const lockerBox = document.createElement('div') lockerBox.classList.add('locker-box') lockerBox.style.display = 'flex' lockerBox.style.alignItems = 'center' lockerBox.style.justifyContent = 'center' const lockerInput = document.createElement('input') lockerInput.id = 'js-dragdrop-input' lockerInput.setAttribute('type', 'checkbox') lockerInput.style.display = 'none' const lockerButton = document.createElement('label') lockerButton.classList.add('btn') lockerButton.id = 'js-dragdrop-label' lockerButton.innerHTML = "DragDrop Lock" lockerButton.setAttribute('for', 'js-dragdrop-input') lockerButton.style.border = '1px solid darkblue' lockerButton.style.cursor = 'pointer' lockerButton.style.padding = '5px' lockerBox.append(lockerInput) lockerBox.append(lockerButton) title.append(lockerBox) // If we set it manually or by default anyway const isLocked = (getCookie('dragDropLock') == 'true' || getCookie('dragDropLock') == '') lockerInput.checked = isLocked lockerInput.addEventListener('change', (e) => { const label = document.getElementById('js-dragdrop-label') e.target.checked ? lock(label) : unlock(label, true) }) resolve(isLocked) }) } const lock = (label) => { label.innerHTML = 'Drag&Drop locked, click to unlock' setLocalCookie('dragDropLock', 'true', 365, location.pathname) const fields = document.querySelectorAll('.moved_field_objects') const dragDropPanel = document.querySelector('td.drag_objects') dragDropPanel.remove() // const rows = document.querySelectorAll('#view_parts_table fieldset') Array.from(fields).map((field) => { const newElement = field.cloneNode(true) field.parentNode.replaceChild(newElement, field) }) const actions = document.querySelectorAll('.field_actions') Array.from(actions).map((action) => action.style.display = 'block') Array.from(document.querySelectorAll('[data-indicator="model_indicator"]')).map((arrow) => arrow.style.display = 'none') } const unlock = (label, reload=false) => { if(reload && !confirm('This will reload current page, proceed ?')) return label.innerHTML = 'Drag&Drop unlocked, click to lock' setLocalCookie('dragDropLock', 'false', 365, location.pathname) if(reload) window.location.reload() } const moveReminders = () => { const tabBox = document.getElementById('mvc_tabs') const helpButton = document.querySelector('#field_types img[alt="Help"]').parentNode const infoButton = document.querySelector('#field_types img[alt="Information"]').parentNode helpButton.querySelector('img').style.marginRight = '5px' infoButton.querySelector('img').style.marginRight = '5px' const helpTab = document.createElement('li') helpTab.classList.add('help-tab') helpTab.style.float = 'right' const helpLabel = document.createElement('span') helpLabel.innerText = 'Help' const infoTab = document.createElement('li') infoTab.classList.add('info-tab') infoTab.style.float = 'right' const infoLabel = document.createElement('span') infoLabel.innerText = 'Infos' helpButton.appendChild(helpLabel) infoButton.appendChild(infoLabel) helpTab.appendChild(helpButton) infoTab.appendChild(infoButton) tabBox.appendChild(infoTab) tabBox.appendChild(helpTab) } if(location.href.includes('/model/')) { moveReminders() addLocker().then((isLocked) => { const label = document.getElementById('js-dragdrop-label') isLocked ? lock(label) : unlock(label) }) } } } // <------ DragDrop Locker // ------> Quality Framework const initQualityFramework = () => { if(modules.qualityFramework.active && !window.location.pathname.includes('/use_cases/')) { const initDevalidationWarning = () => { const submitButtons = [...document.querySelectorAll('input[type="submit"]')] const originalStatus = document.querySelector("select[id$='_status']") if(!submitButtons || !originalStatus) return const isValidated = (originalStatus.value == 8) const isDraft = (originalStatus.value == 1) if(isDraft) { setTimeout(function(){ initDevalidationWarning() return }, 800); } if(!isValidated) { return } submitButtons.map((button) => { button.addEventListener('click', function(e) { if(!button.classList.contains('confirmed')) { e.preventDefault() if (!confirm('Saving a "validated" code will make it regress to "waiting for validation". Continue ?')) return originalStatus.value = 7 button.classList.add('confirmed') button.click() } }) }) } const initFieldsRequired = () => { const description = document.querySelector("textarea[id$='_description']") // const commit = document.querySelector("textarea[id$='_commit_message']") const submitButtons = [...document.querySelectorAll('input[type="submit"]')] if(!submitButtons || !description) return if(description.value.trim().length < 10) { submitButtons.map((button) => { disableElement(button, 'A description of at least 10 characters is required') }) } description.addEventListener('input', () => { handleRequiredFields(submitButtons, description) }) // commit.addEventListener('input', () => { // handleRequiredFields(submitButtons, description, commit) // }) } const handleRequiredFields = (buttons, description) => { if(description.value.trim().length < 10) { buttons.map((button) => { disableElement(button, 'A description and a commit message of at least 10 characters is required') }) } else { buttons.map((button) => { button.removeAttribute('disabled') button.removeAttribute('title') button.removeAttribute('style') }) } } initDevalidationWarning() initFieldsRequired() } } // <------ Quality Framework // ------> Experimental UI const initExperimentalUi = () => { if(modules.experimentalInterface.active) { console.log('%c Loading experimental interface', 'color: #38C4CC') const link = document.createElement('link'); link.type = 'text/css'; link.rel = 'stylesheet'; document.head.appendChild(link); link.href = 'https://cdnjs.cloudflare.com/ajax/libs/chosen/1.8.7/chosen.css'; jQuery('select[multiple="multiple"]').chosen() // jQuery("select[id*='field_format_views_matching_']").chosen() jQuery('.chosen-container-multi') .css('width', '100%') jQuery('.chosen-choices') .css('overflow', 'auto') .css('max-height', '35vh') console.log('test', jQuery('#review_filters').find('select')) jQuery('#review_filters').find('select').chosen() } } // <----- Experimental UI jQuery(document).ready(function() { setTimeout(function () { initOverlay() }, 800); }); })(jQuery); // ------> HELPERS (overlay global) function setLocalCookie(name, value, days, path = '/') { let expires = ''; if (days) { const date = new Date(); date.setTime(date.getTime() + (days*24*60*60*1000)); expires = '; expires=' + date.toUTCString(); } document.cookie = name + '=' + (value || '') + expires + `; path=${path}`; } function getCookie(cname) { const decodedCookie = decodeURIComponent(document.cookie); const ca = decodedCookie.split(';'); const match = ca.filter(cookie => { return cookie.includes(cname) }) return match[0] ? match[0].replace(`${cname}=`, '').trim() : ''; } function disableElement(element, title='') { element.setAttribute('disabled', 'disabled') element.setAttribute('title', title) element.style.color = 'grey' element.style.borderColor = 'grey' element.style.cursor = 'not-allowed' } function delay(time) { return new Promise(resolve => setTimeout(resolve, time)) } // <------ HELPERS