Chris@18: /** Chris@18: * @file Chris@18: * Provides styles for CKEditor inside off-canvas dialogs. Chris@18: */ Chris@18: Chris@18: (($, CKEDITOR) => { Chris@18: /** Chris@18: * Takes a string of CKEditor CSS and modifies it for use in off-canvas. Chris@18: * Chris@18: * @param {string} originalCss Chris@18: * The CSS rules from CKEditor. Chris@18: * @return {string} Chris@18: * The rules from originalCss with extra specificity for off-canvas. Chris@18: */ Chris@18: const convertToOffCanvasCss = originalCss => { Chris@18: const selectorPrefix = '#drupal-off-canvas '; Chris@18: const skinPath = `${CKEDITOR.basePath}${CKEDITOR.skinName}/`; Chris@18: const css = originalCss Chris@18: .substring(originalCss.indexOf('*/') + 2) Chris@18: .trim() Chris@18: .replace(/}/g, `}${selectorPrefix}`) Chris@18: .replace(/,/g, `,${selectorPrefix}`) Chris@18: .replace(/url\(/g, skinPath); Chris@18: return `${selectorPrefix}${css}`; Chris@18: }; Chris@18: Chris@18: /** Chris@18: * Inserts CSS rules into DOM. Chris@18: * Chris@18: * @param {string} cssToInsert Chris@18: * CSS rules to be inserted Chris@18: */ Chris@18: const insertCss = cssToInsert => { Chris@18: const offCanvasCss = document.createElement('style'); Chris@18: offCanvasCss.innerHTML = cssToInsert; Chris@18: offCanvasCss.setAttribute('id', 'ckeditor-off-canvas-reset'); Chris@18: document.body.appendChild(offCanvasCss); Chris@18: }; Chris@18: Chris@18: /** Chris@18: * Adds CSS so CKEditor is styled properly in off-canvas. Chris@18: */ Chris@18: const addCkeditorOffCanvasCss = () => { Chris@18: // If #ckeditor-off-canvas-reset exists, this has already run. Chris@18: if (document.getElementById('ckeditor-off-canvas-reset')) { Chris@18: return; Chris@18: } Chris@18: // CKEDITOR.skin.getPath() requires the CKEDITOR.skinName property. Chris@18: // @see https://stackoverflow.com/a/17336982 Chris@18: CKEDITOR.skinName = CKEDITOR.skin.name; Chris@18: Chris@18: // Get the paths to the css CKEditor is using. Chris@18: const editorCssPath = CKEDITOR.skin.getPath('editor'); Chris@18: const dialogCssPath = CKEDITOR.skin.getPath('dialog'); Chris@18: Chris@18: // The key for cached CSS in localStorage is based on the CSS paths. Chris@18: const storedOffCanvasCss = window.localStorage.getItem( Chris@18: `Drupal.off-canvas.css.${editorCssPath}${dialogCssPath}`, Chris@18: ); Chris@18: Chris@18: // See if CSS is cached in localStorage, and use that when available. Chris@18: if (storedOffCanvasCss) { Chris@18: insertCss(storedOffCanvasCss); Chris@18: return; Chris@18: } Chris@18: Chris@18: // If CSS unavailable in localStorage, get the files via AJAX and parse. Chris@18: $.when($.get(editorCssPath), $.get(dialogCssPath)).done( Chris@18: (editorCss, dialogCss) => { Chris@18: const offCanvasEditorCss = convertToOffCanvasCss(editorCss[0]); Chris@18: const offCanvasDialogCss = convertToOffCanvasCss(dialogCss[0]); Chris@18: const cssToInsert = `#drupal-off-canvas .cke_inner * {background: transparent;} Chris@18: ${offCanvasEditorCss} Chris@18: ${offCanvasDialogCss}`; Chris@18: insertCss(cssToInsert); Chris@18: Chris@18: // The localStorage key for accessing the cached CSS is based on the Chris@18: // paths of the CKEditor CSS files. This prevents localStorage from Chris@18: // providing outdated CSS. If new files are used due to using a new Chris@18: // skin, a new localStorage key is created. Chris@18: // Chris@18: // The CSS paths also include the cache-busting query string that is Chris@18: // stored in state and CKEDITOR.timestamp. This query string changes on Chris@18: // update and cache clear and prevents localStorage from providing Chris@18: // stale CKEditor CSS. Chris@18: // Chris@18: // Before adding the CSS rules to localStorage, there is a check that Chris@18: // confirms the cache-busting query (CKEDITOR.timestamp) is in the CSS Chris@18: // paths. This prevents localStorage from caching something unbustable. Chris@18: // Chris@18: // @see ckeditor_library_info_alter() Chris@18: if ( Chris@18: CKEDITOR.timestamp && Chris@18: editorCssPath.indexOf(CKEDITOR.timestamp) !== -1 && Chris@18: dialogCssPath.indexOf(CKEDITOR.timestamp) !== -1 Chris@18: ) { Chris@18: window.localStorage.setItem( Chris@18: `Drupal.off-canvas.css.${editorCssPath}${dialogCssPath}`, Chris@18: cssToInsert, Chris@18: ); Chris@18: } Chris@18: }, Chris@18: ); Chris@18: }; Chris@18: Chris@18: addCkeditorOffCanvasCss(); Chris@18: })(jQuery, CKEDITOR);