From 407c180cb0c7da088ef68249b5752ca11a697aed Mon Sep 17 00:00:00 2001 From: Ole Poetter <48469242+olpo24@users.noreply.github.com> Date: Thu, 21 May 2026 07:34:42 +0200 Subject: [PATCH 1/4] Update element-code-box.php --- elements/element-code-box.php | 220 ++++++++++++++++++++-------------- 1 file changed, 133 insertions(+), 87 deletions(-) diff --git a/elements/element-code-box.php b/elements/element-code-box.php index 66f590c..6851037 100644 --- a/elements/element-code-box.php +++ b/elements/element-code-box.php @@ -21,7 +21,6 @@ public function set_control_groups() { $this->control_groups['content'] = [ 'title' => esc_html__( 'Content', 'bricks-code-box' ), 'tab' => 'content' ]; $this->control_groups['settings'] = [ 'title' => esc_html__( 'Settings', 'bricks-code-box' ), 'tab' => 'content' ]; $this->control_groups['labels'] = [ 'title' => esc_html__( 'Button Labels', 'bricks-code-box' ), 'tab' => 'content' ]; - // Show style settings direkt im CONTENT-Tab, damit man nicht zwischen Tabs wechseln muss. $this->control_groups['style'] = [ 'title' => esc_html__( 'Style', 'bricks-code-box' ), 'tab' => 'content' ]; } @@ -39,26 +38,43 @@ public function set_controls() { 'default' => 'markup', ]; $this->controls['theme'] = [ - 'tab' => 'content', 'group' => 'settings', 'label' => esc_html__( 'Prism theme', 'bricks-code-box' ), + 'tab' => 'content', 'group' => 'settings', 'label' => esc_html__( 'Prism theme (Light)', 'bricks-code-box' ), 'type' => 'select', 'options' => [ - 'prism' => 'Default (light)', - 'prism-coy' => 'Coy (light)', + 'prism' => 'Default (light)', + 'prism-coy' => 'Coy (light)', 'prism-solarizedlight' => 'Solarized Light', - 'prism-okaidia' => 'Okaidia (dark)', - 'prism-tomorrow' => 'Tomorrow (dark)', - 'prism-twilight' => 'Twilight (dark)', - 'prism-funky' => 'Funky', + 'prism-okaidia' => 'Okaidia (dark)', + 'prism-tomorrow' => 'Tomorrow (dark)', + 'prism-twilight' => 'Twilight (dark)', + 'prism-funky' => 'Funky', ], 'default' => 'prism', ]; + $this->controls['theme_dark'] = [ + 'tab' => 'content', + 'group' => 'settings', + 'label' => esc_html__( 'Prism theme (Dark Mode)', 'bricks-code-box' ), + 'type' => 'select', + 'options' => [ + '' => 'β€” Same as Light β€”', + 'prism' => 'Default (light)', + 'prism-coy' => 'Coy (light)', + 'prism-solarizedlight' => 'Solarized Light', + 'prism-okaidia' => 'Okaidia (dark)', + 'prism-tomorrow' => 'Tomorrow (dark)', + 'prism-twilight' => 'Twilight (dark)', + 'prism-funky' => 'Funky', + ], + 'default' => 'prism-okaidia', + ]; $this->controls['background_color'] = [ - 'tab' => 'content', + 'tab' => 'content', 'group' => 'settings', 'label' => esc_html__( 'Background color (optional)', 'bricks-code-box' ), - 'type' => 'color', + 'type' => 'color', 'default' => '', - 'description' => esc_html__( 'Leave empty to use the Prism theme background. If set, this color is used for the whole code block.', 'bricks-code-box' ), + 'description' => esc_html__( 'Leave empty to use the Prism theme background.', 'bricks-code-box' ), ]; $this->controls['line_numbers'] = [ 'tab' => 'content','group' => 'settings','label' => esc_html__( 'Line numbers', 'bricks-code-box' ), @@ -167,12 +183,17 @@ public function set_controls() { } public function enqueue_scripts() { - // Nur laden wenn noch nicht geladen (Performance-Optimierung) - if ( !wp_style_is('bcb-prism-theme', 'enqueued') ) { - $theme = isset($this->settings['theme']) ? sanitize_text_field($this->settings['theme']) : 'prism'; - $allowed_themes = ['prism', 'prism-coy', 'prism-solarizedlight', 'prism-okaidia', 'prism-tomorrow', 'prism-twilight', 'prism-funky']; - $theme = in_array($theme, $allowed_themes) ? $theme : 'prism'; - + $allowed_themes = ['prism', 'prism-coy', 'prism-solarizedlight', 'prism-okaidia', 'prism-tomorrow', 'prism-twilight', 'prism-funky']; + + // --- Light theme --- + $theme = isset($this->settings['theme']) ? sanitize_text_field($this->settings['theme']) : 'prism'; + $theme = in_array($theme, $allowed_themes) ? $theme : 'prism'; + + // --- Dark theme --- + $theme_dark_raw = isset($this->settings['theme_dark']) ? sanitize_text_field($this->settings['theme_dark']) : ''; + $theme_dark = ( $theme_dark_raw !== '' && in_array($theme_dark_raw, $allowed_themes) ) ? $theme_dark_raw : ''; + + if ( ! wp_style_is('bcb-prism-theme', 'enqueued') ) { $theme_url = 'https://cdn.jsdelivr.net/npm/prismjs/themes/' . $theme . '.min.css'; wp_enqueue_style('bcb-prism-theme', $theme_url, [], '1.29.0'); wp_enqueue_style('bcb-prism-linenumbers','https://cdn.jsdelivr.net/npm/prismjs/plugins/line-numbers/prism-line-numbers.min.css',['bcb-prism-theme'],'1.29.0'); @@ -181,11 +202,17 @@ public function enqueue_scripts() { wp_enqueue_script('bcb-prism-linenumbers','https://cdn.jsdelivr.net/npm/prismjs/plugins/line-numbers/prism-line-numbers.min.js',['bcb-prism-core'],'1.29.0',true); } - // CSS nur einmal laden (Performance-Optimierung) - if ( !wp_style_is('bcb-code-box-inline', 'enqueued') ) { - // Layout & UX only – background/colors come entweder von: - // - Prism theme (Standard) - // - Einer optionalen Custom-Farbe (background_color) + // --- Dark theme stylesheet (only when a different dark theme is selected) --- + if ( $theme_dark !== '' && $theme_dark !== $theme ) { + if ( ! wp_style_is('bcb-prism-theme-dark', 'enqueued') ) { + $theme_dark_url = 'https://cdn.jsdelivr.net/npm/prismjs/themes/' . $theme_dark . '.min.css'; + // Load the dark stylesheet but disable it immediately via JS – it will be + // activated by the dark-mode switcher script below. + wp_enqueue_style('bcb-prism-theme-dark', $theme_dark_url, ['bcb-prism-theme'], '1.29.0'); + } + } + + if ( ! wp_style_is('bcb-code-box-inline', 'enqueued') ) { $css = '.bcb-code-box-wrapper{position:relative;display:block;width:100%;box-sizing:border-box;overflow:auto;padding:1em;border-radius:8px;max-height:var(--bcb-max-height,400px)}' .'.bcb-code-box-wrapper.is-full{max-height:none;overflow:visible}' .'.bcb-code-box-wrapper pre{margin:0;width:100%;box-sizing:border-box;white-space:pre-wrap;word-break:break-word;overflow-wrap:anywhere}' @@ -195,7 +222,6 @@ public function enqueue_scripts() { .'.bcb-code-box-wrapper .copy-btn:hover{background:var(--bcb-btn-bg-hover,transparent);border-color:var(--bcb-btn-border-color-hover,#666);color:var(--bcb-btn-color-hover,#666)}' .'.bcb-code-box-wrapper .filename{position:absolute;top:8px;left:8px;background:rgba(0,0,0,0.1);color:#666;padding:2px 8px;border-radius:4px;font-size:11px;font-family:ui-monospace,Menlo,Monaco,Consolas,"Liberation Mono",monospace;z-index:1}' .'.bcb-code-box-wrapper.has-filename .copy-btn{right:8px;top:8px}' - // Custom background: einfarbiger Block, Prism-Decor (BG, Shadow, Border) wird neutralisiert .'.bcb-code-box-wrapper.has-custom-bg,.bcb-code-box-wrapper.has-custom-bg pre[class*="language-"],.bcb-code-box-wrapper.has-custom-bg code[class*="language-"]{background:var(--bcb-bg-color,#f5f5f5)!important;background-image:none!important;box-shadow:none!important;border:none!important;outline:none!important}' .'.bcb-code-box-wrapper.has-custom-bg.line-numbers .line-numbers-rows > span:before{background:transparent!important;border:none!important;box-shadow:none!important}' .'.bcb-code-box-wrapper.has-custom-bg.line-numbers pre[class*="language-"]{padding-left:3.8em}'; @@ -204,12 +230,81 @@ public function enqueue_scripts() { wp_add_inline_style('bcb-code-box-inline', $css); } - // JavaScript nur einmal laden (Performance-Optimierung) - if ( !wp_script_is('bcb-code-box-init', 'enqueued') ) { + if ( ! wp_script_is('bcb-code-box-init', 'enqueued') ) { $js = << + if(window.MutationObserver){ + var observer=new MutationObserver(function(mutations){ + mutations.forEach(function(m){ + if(m.type==='attributes'&&m.attributeName==='data-brx-theme'){applyDarkTheme();} + }); + }); + observer.observe(document.documentElement,{attributes:true,attributeFilter:['data-brx-theme']}); + } + + if(document.readyState==='loading'){ + document.addEventListener('DOMContentLoaded',function(){initAllCodeBoxes();applyDarkTheme();}); + }else{ + initAllCodeBoxes();applyDarkTheme(); + } + if(window.bricksIsFrontend){document.addEventListener('bricks/frontend/init',function(){initAllCodeBoxes();applyDarkTheme();});} +})(); JS; - wp_register_script('bcb-code-box-init', '', [], '1.0.2', true); + wp_register_script('bcb-code-box-init', '', [], '1.0.3', true); wp_enqueue_script('bcb-code-box-init'); wp_add_inline_script('bcb-code-box-init', $js); } @@ -217,7 +312,6 @@ public function enqueue_scripts() { public function render() { $settings = $this->settings; - // Keep code exactly as entered; escaping happens at output (esc_html / esc_textarea) $code = isset($settings['code']) ? (string) $settings['code'] : ''; $language = isset($settings['language']) ? sanitize_text_field($settings['language']) : 'markup'; $lineNumbers= !empty($settings['line_numbers']); @@ -227,8 +321,7 @@ public function render() { $full_height = !empty($settings['full_height']); $show_filename = !empty($settings['show_filename']); $filename = isset($settings['filename']) ? sanitize_text_field($settings['filename']) : ''; - - // Validiere Sprache gegen erlaubte Werte + $allowed_languages = ['markup', 'javascript', 'php', 'css', 'java', 'python', 'sql', 'bash']; $language = in_array($language, $allowed_languages) ? $language : 'markup'; @@ -236,10 +329,8 @@ public function render() { if ( $lineNumbers ) { $root_classes[] = 'line-numbers'; } if ( $show_filename && !empty($filename) ) { $root_classes[] = 'has-filename'; } if ( $full_height ) { $root_classes[] = 'is-full'; } - - // Optional custom background color: if set, we use it for the whole block + $bg_color_raw = $settings['background_color'] ?? ''; - // Bricks color control kann als Array kommen (z.B. ['hex' => '#ff0000']) if ( is_array($bg_color_raw) ) { $bg_color_raw = (string) reset($bg_color_raw); } @@ -248,72 +339,27 @@ public function render() { if ( $has_custom_bg ) { $root_classes[] = 'has-custom-bg'; } - - // Copy button style variables - $copy_btn_position = isset($settings['copy_btn_position']) ? sanitize_text_field($settings['copy_btn_position']) : 'right'; - $copy_btn_offset_y = isset($settings['copy_btn_offset_y']) ? max(0, intval($settings['copy_btn_offset_y'])) : 8; - $copy_btn_offset_x = isset($settings['copy_btn_offset_x']) ? max(0, intval($settings['copy_btn_offset_x'])) : 8; + + $copy_btn_position = isset($settings['copy_btn_position']) ? sanitize_text_field($settings['copy_btn_position']) : 'right'; + $copy_btn_offset_y = isset($settings['copy_btn_offset_y']) ? max(0, intval($settings['copy_btn_offset_y'])) : 8; + $copy_btn_offset_x = isset($settings['copy_btn_offset_x']) ? max(0, intval($settings['copy_btn_offset_x'])) : 8; $copy_btn_font_size = isset($settings['copy_btn_font_size']) ? max(8, min(24, intval($settings['copy_btn_font_size']))) : 12; - // Bricks color controls kΓΆnnen Arrays liefern – wir nehmen einfach den ersten String-Wert $copy_btn_bg_raw = $settings['copy_btn_bg'] ?? ''; $copy_btn_color_raw = $settings['copy_btn_color'] ?? ''; $copy_btn_border_raw= $settings['copy_btn_border_color'] ?? ''; - $copy_btn_bg = is_array($copy_btn_bg_raw) ? (string) reset($copy_btn_bg_raw) : (string) $copy_btn_bg_raw; - $copy_btn_color = is_array($copy_btn_color_raw) ? (string) reset($copy_btn_color_raw) : (string) $copy_btn_color_raw; + $copy_btn_bg = is_array($copy_btn_bg_raw) ? (string) reset($copy_btn_bg_raw) : (string) $copy_btn_bg_raw; + $copy_btn_color = is_array($copy_btn_color_raw) ? (string) reset($copy_btn_color_raw) : (string) $copy_btn_color_raw; $copy_btn_border = is_array($copy_btn_border_raw) ? (string) reset($copy_btn_border_raw) : (string) $copy_btn_border_raw; if ( $copy_btn_position === 'left' ) { $root_classes[] = 'copy-btn-left'; } - - $style_attr = '--bcb-font-size: ' . esc_attr($font_size) . 'px; --bcb-max-height: ' . esc_attr($max_height) . 'px;'; + + $style_attr = '--bcb-font-size: ' . esc_attr($font_size) . 'px; --bcb-max-height: ' . esc_attr($max_height) . 'px;'; $style_attr .= ' --bcb-btn-top: ' . esc_attr($copy_btn_offset_y) . 'px;'; $style_attr .= ' --bcb-btn-offset-x: ' . esc_attr($copy_btn_offset_x) . 'px;'; $style_attr .= ' --bcb-btn-font-size: ' . esc_attr($copy_btn_font_size) . 'px;'; if ( $copy_btn_bg !== '' ) { - $style_attr .= ' --bcb-btn-bg: ' . esc_attr($copy_btn_bg) . ';'; - // leichte Hover-Variante andeuten (kann der User bei Bedarf ΓΌberschreiben) - $style_attr .= ' --bcb-btn-bg-hover: ' . esc_attr($copy_btn_bg) . ';'; - } - if ( $copy_btn_color !== '' ) { - $style_attr .= ' --bcb-btn-color: ' . esc_attr($copy_btn_color) . ';'; - $style_attr .= ' --bcb-btn-color-hover: ' . esc_attr($copy_btn_color) . ';'; - } - if ( $copy_btn_border !== '' ) { - $style_attr .= ' --bcb-btn-border-color: ' . esc_attr($copy_btn_border) . ';'; - $style_attr .= ' --bcb-btn-border-color-hover: ' . esc_attr($copy_btn_border) . ';'; - } - if ( $has_custom_bg ) { - $style_attr .= ' --bcb-bg-color: ' . esc_attr($bg_color) . ';'; - } - - $this->set_attribute('_root', 'class', $root_classes); - $this->set_attribute('_root', 'style', $style_attr); - $this->set_attribute('_root', 'data-lang', esc_attr($language)); - $this->set_attribute('_root', 'data-copy', $showCopy ? '1' : '0'); - $this->set_attribute('_root', 'data-label-de', esc_attr(sanitize_text_field($settings['label_copy_de'] ?? 'πŸ“‹ Kopieren'))); - $this->set_attribute('_root', 'data-done-de', esc_attr(sanitize_text_field($settings['label_done_de'] ?? 'βœ… Kopiert!'))); - $this->set_attribute('_root', 'data-label-en', esc_attr(sanitize_text_field($settings['label_copy_en'] ?? 'πŸ“‹ Copy'))); - $this->set_attribute('_root', 'data-done-en', esc_attr(sanitize_text_field($settings['label_done_en'] ?? 'βœ… Copied!'))); - - echo '
render_attributes('_root') . '>'; - if ( $show_filename && !empty($filename) ) { - echo '
' . esc_html($filename) . '
'; - } - if ( $showCopy ) { - $locale = function_exists('determine_locale') ? determine_locale() : get_locale(); - $is_en = is_string($locale) && strpos(strtolower($locale), 'en') === 0; - $initialLabel = $is_en ? sanitize_text_field($settings['label_copy_en'] ?? 'πŸ“‹ Copy') : sanitize_text_field($settings['label_copy_de'] ?? 'πŸ“‹ Kopieren'); - echo ''; - } - $pre_classes = $lineNumbers ? 'line-numbers' : ''; - echo '
' . esc_html($code) . '
'; - echo ''; - echo '
'; - } - } -} - - + $style_attr .= ' --bcb-btn-bg: ' . esc_attr($copy_btn_ From 76a41a83b5a96e3ae4a6a5f85b275aa90757038b Mon Sep 17 00:00:00 2001 From: Ole Poetter <48469242+olpo24@users.noreply.github.com> Date: Thu, 21 May 2026 07:36:04 +0200 Subject: [PATCH 2/4] Add custom styles for copy button and background --- elements/element-code-box.php | 44 ++++++++++++++++++++++++++++++++++- 1 file changed, 43 insertions(+), 1 deletion(-) diff --git a/elements/element-code-box.php b/elements/element-code-box.php index 6851037..429a2a0 100644 --- a/elements/element-code-box.php +++ b/elements/element-code-box.php @@ -362,4 +362,46 @@ public function render() { $style_attr .= ' --bcb-btn-offset-x: ' . esc_attr($copy_btn_offset_x) . 'px;'; $style_attr .= ' --bcb-btn-font-size: ' . esc_attr($copy_btn_font_size) . 'px;'; if ( $copy_btn_bg !== '' ) { - $style_attr .= ' --bcb-btn-bg: ' . esc_attr($copy_btn_ + $style_attr .= ' --bcb-btn-bg: ' . esc_attr($copy_btn_bg) . ';'; + $style_attr .= ' --bcb-btn-bg-hover: ' . esc_attr($copy_btn_bg) . ';'; + } + if ( $copy_btn_color !== '' ) { + $style_attr .= ' --bcb-btn-color: ' . esc_attr($copy_btn_color) . ';'; + $style_attr .= ' --bcb-btn-color-hover: ' . esc_attr($copy_btn_color) . ';'; + } + if ( $copy_btn_border !== '' ) { + $style_attr .= ' --bcb-btn-border-color: ' . esc_attr($copy_btn_border) . ';'; + $style_attr .= ' --bcb-btn-border-color-hover: ' . esc_attr($copy_btn_border) . ';'; + } + if ( $has_custom_bg ) { + $style_attr .= ' --bcb-bg-color: ' . esc_attr($bg_color) . ';'; + } + + $this->set_attribute('_root', 'class', $root_classes); + $this->set_attribute('_root', 'style', $style_attr); + $this->set_attribute('_root', 'data-lang', esc_attr($language)); + $this->set_attribute('_root', 'data-copy', $showCopy ? '1' : '0'); + $this->set_attribute('_root', 'data-label-de', esc_attr(sanitize_text_field($settings['label_copy_de'] ?? 'πŸ“‹ Kopieren'))); + $this->set_attribute('_root', 'data-done-de', esc_attr(sanitize_text_field($settings['label_done_de'] ?? 'βœ… Kopiert!'))); + $this->set_attribute('_root', 'data-label-en', esc_attr(sanitize_text_field($settings['label_copy_en'] ?? 'πŸ“‹ Copy'))); + $this->set_attribute('_root', 'data-done-en', esc_attr(sanitize_text_field($settings['label_done_en'] ?? 'βœ… Copied!'))); + + echo '
render_attributes('_root') . '>'; + if ( $show_filename && !empty($filename) ) { + echo '
' . esc_html($filename) . '
'; + } + if ( $showCopy ) { + $locale = function_exists('determine_locale') ? determine_locale() : get_locale(); + $is_en = is_string($locale) && strpos(strtolower($locale), 'en') === 0; + $initialLabel = $is_en + ? sanitize_text_field($settings['label_copy_en'] ?? 'πŸ“‹ Copy') + : sanitize_text_field($settings['label_copy_de'] ?? 'πŸ“‹ Kopieren'); + echo ''; + } + $pre_classes = $lineNumbers ? 'line-numbers' : ''; + echo '
' . esc_html($code) . '
'; + echo ''; + echo '
'; + } + } +} From 82d3b7b7d9d15c4617a4da72092cb5777e38c877 Mon Sep 17 00:00:00 2001 From: Ole Poetter <48469242+olpo24@users.noreply.github.com> Date: Thu, 21 May 2026 07:53:38 +0200 Subject: [PATCH 3/4] Change loading prism.js assets from cdn to local --- elements/element-code-box.php | 619 +++++++++++++++++++++------------- 1 file changed, 377 insertions(+), 242 deletions(-) diff --git a/elements/element-code-box.php b/elements/element-code-box.php index 429a2a0..95949c5 100644 --- a/elements/element-code-box.php +++ b/elements/element-code-box.php @@ -18,229 +18,360 @@ public function get_keywords() { } public function set_control_groups() { - $this->control_groups['content'] = [ 'title' => esc_html__( 'Content', 'bricks-code-box' ), 'tab' => 'content' ]; - $this->control_groups['settings'] = [ 'title' => esc_html__( 'Settings', 'bricks-code-box' ), 'tab' => 'content' ]; - $this->control_groups['labels'] = [ 'title' => esc_html__( 'Button Labels', 'bricks-code-box' ), 'tab' => 'content' ]; - $this->control_groups['style'] = [ 'title' => esc_html__( 'Style', 'bricks-code-box' ), 'tab' => 'content' ]; + $this->control_groups['content'] = [ 'title' => esc_html__( 'Content', 'bricks-code-box' ), 'tab' => 'content' ]; + $this->control_groups['settings'] = [ 'title' => esc_html__( 'Settings', 'bricks-code-box' ), 'tab' => 'content' ]; + $this->control_groups['labels'] = [ 'title' => esc_html__( 'Button Labels', 'bricks-code-box' ), 'tab' => 'content' ]; + $this->control_groups['style'] = [ 'title' => esc_html__( 'Style', 'bricks-code-box' ), 'tab' => 'content' ]; } public function set_controls() { + + /* ── Content ─────────────────────────────────────────────────── */ + $this->controls['code'] = [ - 'tab' => 'content', 'group' => 'content', 'label' => esc_html__( 'Code', 'bricks-code-box' ), - 'type' => 'textarea', 'rows' => 16, 'default' => "// your code here\n", + 'tab' => 'content', + 'group' => 'content', + 'label' => esc_html__( 'Code', 'bricks-code-box' ), + 'type' => 'textarea', + 'rows' => 16, + 'default' => "// your code here\n", ]; + + /* ── Settings ────────────────────────────────────────────────── */ + $this->controls['language'] = [ - 'tab' => 'content', 'group' => 'settings', 'label' => esc_html__( 'Language', 'bricks-code-box' ), - 'type' => 'select', + 'tab' => 'content', + 'group' => 'settings', + 'label' => esc_html__( 'Language', 'bricks-code-box' ), + 'type' => 'select', 'options' => [ - 'markup' => 'Markup (HTML/XML)','javascript' => 'JavaScript','php' => 'PHP','css' => 'CSS','java' => 'Java','python' => 'Python','sql' => 'SQL','bash' => 'Bash/Shell', + 'markup' => 'Markup (HTML/XML)', + 'javascript' => 'JavaScript', + 'php' => 'PHP', + 'css' => 'CSS', + 'java' => 'Java', + 'python' => 'Python', + 'sql' => 'SQL', + 'bash' => 'Bash/Shell', ], 'default' => 'markup', ]; + + $theme_options = [ + 'prism' => 'Default (light)', + 'prism-coy' => 'Coy (light)', + 'prism-solarizedlight' => 'Solarized Light', + 'prism-okaidia' => 'Okaidia (dark)', + 'prism-tomorrow' => 'Tomorrow (dark)', + 'prism-twilight' => 'Twilight (dark)', + 'prism-funky' => 'Funky', + ]; + $this->controls['theme'] = [ - 'tab' => 'content', 'group' => 'settings', 'label' => esc_html__( 'Prism theme (Light)', 'bricks-code-box' ), - 'type' => 'select', - 'options' => [ - 'prism' => 'Default (light)', - 'prism-coy' => 'Coy (light)', - 'prism-solarizedlight' => 'Solarized Light', - 'prism-okaidia' => 'Okaidia (dark)', - 'prism-tomorrow' => 'Tomorrow (dark)', - 'prism-twilight' => 'Twilight (dark)', - 'prism-funky' => 'Funky', - ], + 'tab' => 'content', + 'group' => 'settings', + 'label' => esc_html__( 'Prism theme (Light)', 'bricks-code-box' ), + 'type' => 'select', + 'options' => $theme_options, 'default' => 'prism', ]; + $this->controls['theme_dark'] = [ 'tab' => 'content', 'group' => 'settings', 'label' => esc_html__( 'Prism theme (Dark Mode)', 'bricks-code-box' ), 'type' => 'select', - 'options' => [ - '' => 'β€” Same as Light β€”', - 'prism' => 'Default (light)', - 'prism-coy' => 'Coy (light)', - 'prism-solarizedlight' => 'Solarized Light', - 'prism-okaidia' => 'Okaidia (dark)', - 'prism-tomorrow' => 'Tomorrow (dark)', - 'prism-twilight' => 'Twilight (dark)', - 'prism-funky' => 'Funky', - ], + 'options' => array_merge( [ '' => 'β€” Same as Light β€”' ], $theme_options ), 'default' => 'prism-okaidia', + 'description' => esc_html__( 'Used when is set.', 'bricks-code-box' ), ]; + $this->controls['background_color'] = [ - 'tab' => 'content', - 'group' => 'settings', - 'label' => esc_html__( 'Background color (optional)', 'bricks-code-box' ), - 'type' => 'color', - 'default' => '', + 'tab' => 'content', + 'group' => 'settings', + 'label' => esc_html__( 'Background color (optional)', 'bricks-code-box' ), + 'type' => 'color', + 'default' => '', 'description' => esc_html__( 'Leave empty to use the Prism theme background.', 'bricks-code-box' ), ]; + $this->controls['line_numbers'] = [ - 'tab' => 'content','group' => 'settings','label' => esc_html__( 'Line numbers', 'bricks-code-box' ), - 'type' => 'checkbox','default' => true, + 'tab' => 'content', + 'group' => 'settings', + 'label' => esc_html__( 'Line numbers', 'bricks-code-box' ), + 'type' => 'checkbox', + 'default' => true, ]; + $this->controls['show_copy'] = [ - 'tab' => 'content','group' => 'settings','label' => esc_html__( 'Show copy button', 'bricks-code-box' ), - 'type' => 'checkbox','default' => true, + 'tab' => 'content', + 'group' => 'settings', + 'label' => esc_html__( 'Show copy button', 'bricks-code-box' ), + 'type' => 'checkbox', + 'default' => true, + ]; + + $this->controls['full_height'] = [ + 'tab' => 'content', + 'group' => 'settings', + 'label' => esc_html__( 'Show full code (no limit)', 'bricks-code-box' ), + 'type' => 'checkbox', + 'default' => false, ]; + + $this->controls['show_filename'] = [ + 'tab' => 'content', + 'group' => 'settings', + 'label' => esc_html__( 'Show filename', 'bricks-code-box' ), + 'type' => 'checkbox', + 'default' => false, + ]; + + $this->controls['filename'] = [ + 'tab' => 'content', + 'group' => 'settings', + 'label' => esc_html__( 'Filename', 'bricks-code-box' ), + 'type' => 'text', + 'default' => '', + 'required' => [ 'show_filename', '=', true ], + ]; + + /* ── Button Labels ───────────────────────────────────────────── */ + $this->controls['label_copy_de'] = [ - 'tab'=>'content','group'=>'labels','label'=>esc_html__( 'Copy label (DE)', 'bricks-code-box' ), - 'type'=>'text','default'=>'πŸ“‹ Kopieren', + 'tab' => 'content', + 'group' => 'labels', + 'label' => esc_html__( 'Copy label (DE)', 'bricks-code-box' ), + 'type' => 'text', + 'default' => 'πŸ“‹ Kopieren', ]; $this->controls['label_done_de'] = [ - 'tab'=>'content','group'=>'labels','label'=>esc_html__( 'Copied label (DE)', 'bricks-code-box' ), - 'type'=>'text','default'=>'βœ… Kopiert!', + 'tab' => 'content', + 'group' => 'labels', + 'label' => esc_html__( 'Copied label (DE)', 'bricks-code-box' ), + 'type' => 'text', + 'default' => 'βœ… Kopiert!', ]; $this->controls['label_copy_en'] = [ - 'tab'=>'content','group'=>'labels','label'=>esc_html__( 'Copy label (EN)', 'bricks-code-box' ), - 'type'=>'text','default'=>'πŸ“‹ Copy', + 'tab' => 'content', + 'group' => 'labels', + 'label' => esc_html__( 'Copy label (EN)', 'bricks-code-box' ), + 'type' => 'text', + 'default' => 'πŸ“‹ Copy', ]; $this->controls['label_done_en'] = [ - 'tab'=>'content','group'=>'labels','label'=>esc_html__( 'Copied label (EN)', 'bricks-code-box' ), - 'type'=>'text','default'=>'βœ… Copied!', + 'tab' => 'content', + 'group' => 'labels', + 'label' => esc_html__( 'Copied label (EN)', 'bricks-code-box' ), + 'type' => 'text', + 'default' => 'βœ… Copied!', ]; - // Copy button style + + /* ── Style ───────────────────────────────────────────────────── */ + $this->controls['copy_btn_position'] = [ - 'tab' => 'content', - 'group' => 'style', - 'label' => esc_html__( 'Copy button position', 'bricks-code-box' ), - 'type' => 'select', + 'tab' => 'content', + 'group' => 'style', + 'label' => esc_html__( 'Copy button position', 'bricks-code-box' ), + 'type' => 'select', 'options' => [ 'right' => esc_html__( 'Top right', 'bricks-code-box' ), - 'left' => esc_html__( 'Top left', 'bricks-code-box' ), + 'left' => esc_html__( 'Top left', 'bricks-code-box' ), ], 'default' => 'right', ]; $this->controls['copy_btn_offset_y'] = [ - 'tab' => 'content', - 'group' => 'style', - 'label' => esc_html__( 'Copy button offset (top, px)', 'bricks-code-box' ), - 'type' => 'number', + 'tab' => 'content', + 'group' => 'style', + 'label' => esc_html__( 'Copy button offset (top, px)', 'bricks-code-box' ), + 'type' => 'number', 'default' => 8, - 'unit' => 'px', + 'unit' => 'px', ]; $this->controls['copy_btn_offset_x'] = [ - 'tab' => 'content', - 'group' => 'style', - 'label' => esc_html__( 'Copy button offset (side, px)', 'bricks-code-box' ), - 'type' => 'number', + 'tab' => 'content', + 'group' => 'style', + 'label' => esc_html__( 'Copy button offset (side, px)', 'bricks-code-box' ), + 'type' => 'number', 'default' => 8, - 'unit' => 'px', + 'unit' => 'px', ]; $this->controls['copy_btn_bg'] = [ - 'tab' => 'content', - 'group' => 'style', - 'label' => esc_html__( 'Copy button background', 'bricks-code-box' ), - 'type' => 'color', - 'default' => '', + 'tab' => 'content', + 'group' => 'style', + 'label' => esc_html__( 'Copy button background', 'bricks-code-box' ), + 'type' => 'color', + 'default' => '', 'description' => esc_html__( 'Leave empty for transparent background.', 'bricks-code-box' ), ]; $this->controls['copy_btn_color'] = [ - 'tab' => 'content', - 'group' => 'style', - 'label' => esc_html__( 'Copy button text color', 'bricks-code-box' ), - 'type' => 'color', + 'tab' => 'content', + 'group' => 'style', + 'label' => esc_html__( 'Copy button text color', 'bricks-code-box' ), + 'type' => 'color', 'default' => '', ]; $this->controls['copy_btn_border_color'] = [ - 'tab' => 'content', - 'group' => 'style', - 'label' => esc_html__( 'Copy button border color', 'bricks-code-box' ), - 'type' => 'color', + 'tab' => 'content', + 'group' => 'style', + 'label' => esc_html__( 'Copy button border color', 'bricks-code-box' ), + 'type' => 'color', 'default' => '', ]; $this->controls['copy_btn_font_size'] = [ - 'tab' => 'content', - 'group' => 'style', - 'label' => esc_html__( 'Copy button font size (px)', 'bricks-code-box' ), - 'type' => 'number', + 'tab' => 'content', + 'group' => 'style', + 'label' => esc_html__( 'Copy button font size (px)', 'bricks-code-box' ), + 'type' => 'number', 'default' => 12, - 'unit' => 'px', + 'unit' => 'px', ]; $this->controls['font_size'] = [ - 'tab'=>'content','group'=>'style','label'=>esc_html__( 'Font size (px)', 'bricks-code-box' ), - 'type'=>'number','default'=>14,'unit'=>'px', + 'tab' => 'content', + 'group' => 'style', + 'label' => esc_html__( 'Font size (px)', 'bricks-code-box' ), + 'type' => 'number', + 'default' => 14, + 'unit' => 'px', ]; $this->controls['max_height'] = [ - 'tab'=>'content','group'=>'style','label'=>esc_html__( 'Max height (px)', 'bricks-code-box' ), - 'type'=>'number','default'=>400,'unit'=>'px', + 'tab' => 'content', + 'group' => 'style', + 'label' => esc_html__( 'Max height (px)', 'bricks-code-box' ), + 'type' => 'number', + 'default' => 400, + 'unit' => 'px', 'description' => esc_html__( 'Set to 0 for no limit', 'bricks-code-box' ), ]; - $this->controls['full_height'] = [ - 'tab'=>'content','group'=>'settings','label'=>esc_html__( 'Show full code (no limit)', 'bricks-code-box' ), - 'type'=>'checkbox','default'=>false, - ]; - $this->controls['show_filename'] = [ - 'tab'=>'content','group'=>'settings','label'=>esc_html__( 'Show filename', 'bricks-code-box' ), - 'type'=>'checkbox','default'=>false, - ]; - $this->controls['filename'] = [ - 'tab'=>'content','group'=>'settings','label'=>esc_html__( 'Filename', 'bricks-code-box' ), - 'type'=>'text','default'=>'', - 'required' => ['show_filename', '=', true], + } + + /* ── Asset helpers ───────────────────────────────────────────────── */ + + /** + * Base URL for the plugin's local assets folder. + * assets/prism/ β†’ JS + line-numbers CSS + * assets/prism/themes/ β†’ theme CSS files + */ + private function assets_url( string $relative ): string { + return plugin_dir_url( dirname( __FILE__ ) ) . 'assets/prism/' . ltrim( $relative, '/' ); + } + + /** Prism version constant – bump when updating the local files. */ + const PRISM_VERSION = '1.29.0'; + + /** All valid theme slugs. */ + private function allowed_themes(): array { + return [ + 'prism', + 'prism-coy', + 'prism-solarizedlight', + 'prism-okaidia', + 'prism-tomorrow', + 'prism-twilight', + 'prism-funky', ]; } + /* ── Enqueue ─────────────────────────────────────────────────────── */ + public function enqueue_scripts() { - $allowed_themes = ['prism', 'prism-coy', 'prism-solarizedlight', 'prism-okaidia', 'prism-tomorrow', 'prism-twilight', 'prism-funky']; - - // --- Light theme --- - $theme = isset($this->settings['theme']) ? sanitize_text_field($this->settings['theme']) : 'prism'; - $theme = in_array($theme, $allowed_themes) ? $theme : 'prism'; - - // --- Dark theme --- - $theme_dark_raw = isset($this->settings['theme_dark']) ? sanitize_text_field($this->settings['theme_dark']) : ''; - $theme_dark = ( $theme_dark_raw !== '' && in_array($theme_dark_raw, $allowed_themes) ) ? $theme_dark_raw : ''; - - if ( ! wp_style_is('bcb-prism-theme', 'enqueued') ) { - $theme_url = 'https://cdn.jsdelivr.net/npm/prismjs/themes/' . $theme . '.min.css'; - wp_enqueue_style('bcb-prism-theme', $theme_url, [], '1.29.0'); - wp_enqueue_style('bcb-prism-linenumbers','https://cdn.jsdelivr.net/npm/prismjs/plugins/line-numbers/prism-line-numbers.min.css',['bcb-prism-theme'],'1.29.0'); - wp_enqueue_script('bcb-prism-core','https://cdn.jsdelivr.net/npm/prismjs/prism.min.js',[],'1.29.0',true); - wp_enqueue_script('bcb-prism-autoloader','https://cdn.jsdelivr.net/npm/prismjs/plugins/autoloader/prism-autoloader.min.js',['bcb-prism-core'],'1.29.0',true); - wp_enqueue_script('bcb-prism-linenumbers','https://cdn.jsdelivr.net/npm/prismjs/plugins/line-numbers/prism-line-numbers.min.js',['bcb-prism-core'],'1.29.0',true); + + $allowed = $this->allowed_themes(); + + /* Light theme */ + $theme = isset( $this->settings['theme'] ) ? sanitize_text_field( $this->settings['theme'] ) : 'prism'; + $theme = in_array( $theme, $allowed, true ) ? $theme : 'prism'; + + /* Dark theme (empty string = "same as light", no extra stylesheet) */ + $theme_dark_raw = isset( $this->settings['theme_dark'] ) ? sanitize_text_field( $this->settings['theme_dark'] ) : ''; + $theme_dark = ( $theme_dark_raw !== '' && in_array( $theme_dark_raw, $allowed, true ) ) ? $theme_dark_raw : ''; + + /* ── Core JS (once per page) ── */ + if ( ! wp_script_is( 'bcb-prism-core', 'enqueued' ) ) { + wp_enqueue_script( + 'bcb-prism-core', + $this->assets_url( 'prism.min.js' ), + [], + self::PRISM_VERSION, + true + ); + wp_enqueue_script( + 'bcb-prism-autoloader', + $this->assets_url( 'prism-autoloader.min.js' ), + [ 'bcb-prism-core' ], + self::PRISM_VERSION, + true + ); + wp_enqueue_script( + 'bcb-prism-linenumbers', + $this->assets_url( 'prism-line-numbers.min.js' ), + [ 'bcb-prism-core' ], + self::PRISM_VERSION, + true + ); } - // --- Dark theme stylesheet (only when a different dark theme is selected) --- + /* ── Light theme CSS (once per page) ── */ + if ( ! wp_style_is( 'bcb-prism-theme', 'enqueued' ) ) { + wp_enqueue_style( + 'bcb-prism-theme', + $this->assets_url( 'themes/' . $theme . '.min.css' ), + [], + self::PRISM_VERSION + ); + wp_enqueue_style( + 'bcb-prism-linenumbers', + $this->assets_url( 'prism-line-numbers.min.css' ), + [ 'bcb-prism-theme' ], + self::PRISM_VERSION + ); + } + + /* ── Dark theme CSS (only when a different dark theme is chosen) ── */ if ( $theme_dark !== '' && $theme_dark !== $theme ) { - if ( ! wp_style_is('bcb-prism-theme-dark', 'enqueued') ) { - $theme_dark_url = 'https://cdn.jsdelivr.net/npm/prismjs/themes/' . $theme_dark . '.min.css'; - // Load the dark stylesheet but disable it immediately via JS – it will be - // activated by the dark-mode switcher script below. - wp_enqueue_style('bcb-prism-theme-dark', $theme_dark_url, ['bcb-prism-theme'], '1.29.0'); + if ( ! wp_style_is( 'bcb-prism-theme-dark', 'enqueued' ) ) { + wp_enqueue_style( + 'bcb-prism-theme-dark', + $this->assets_url( 'themes/' . $theme_dark . '.min.css' ), + [ 'bcb-prism-theme' ], + self::PRISM_VERSION + ); } } - if ( ! wp_style_is('bcb-code-box-inline', 'enqueued') ) { - $css = '.bcb-code-box-wrapper{position:relative;display:block;width:100%;box-sizing:border-box;overflow:auto;padding:1em;border-radius:8px;max-height:var(--bcb-max-height,400px)}' - .'.bcb-code-box-wrapper.is-full{max-height:none;overflow:visible}' - .'.bcb-code-box-wrapper pre{margin:0;width:100%;box-sizing:border-box;white-space:pre-wrap;word-break:break-word;overflow-wrap:anywhere}' - .'.bcb-code-box-wrapper code{font-family:ui-monospace,Menlo,Monaco,Consolas,"Liberation Mono",monospace;font-size:var(--bcb-font-size,14px);display:block;max-width:100%;white-space:pre-wrap;word-break:break-word;overflow-wrap:anywhere}' - .'.bcb-code-box-wrapper .copy-btn{position:absolute;top:var(--bcb-btn-top,8px);right:var(--bcb-btn-offset-x,8px);left:auto;background:var(--bcb-btn-bg,transparent);color:var(--bcb-btn-color,#444);border:1px solid var(--bcb-btn-border-color,#444);padding:4px 10px;border-radius:4px;cursor:pointer!important;font-size:var(--bcb-btn-font-size,12px);z-index:9999;pointer-events:auto}' - .'.bcb-code-box-wrapper.copy-btn-left .copy-btn{right:auto;left:var(--bcb-btn-offset-x,8px)}' - .'.bcb-code-box-wrapper .copy-btn:hover{background:var(--bcb-btn-bg-hover,transparent);border-color:var(--bcb-btn-border-color-hover,#666);color:var(--bcb-btn-color-hover,#666)}' - .'.bcb-code-box-wrapper .filename{position:absolute;top:8px;left:8px;background:rgba(0,0,0,0.1);color:#666;padding:2px 8px;border-radius:4px;font-size:11px;font-family:ui-monospace,Menlo,Monaco,Consolas,"Liberation Mono",monospace;z-index:1}' - .'.bcb-code-box-wrapper.has-filename .copy-btn{right:8px;top:8px}' - .'.bcb-code-box-wrapper.has-custom-bg,.bcb-code-box-wrapper.has-custom-bg pre[class*="language-"],.bcb-code-box-wrapper.has-custom-bg code[class*="language-"]{background:var(--bcb-bg-color,#f5f5f5)!important;background-image:none!important;box-shadow:none!important;border:none!important;outline:none!important}' - .'.bcb-code-box-wrapper.has-custom-bg.line-numbers .line-numbers-rows > span:before{background:transparent!important;border:none!important;box-shadow:none!important}' - .'.bcb-code-box-wrapper.has-custom-bg.line-numbers pre[class*="language-"]{padding-left:3.8em}'; - wp_register_style('bcb-code-box-inline', false); - wp_enqueue_style('bcb-code-box-inline'); - wp_add_inline_style('bcb-code-box-inline', $css); + /* ── Layout CSS (once per page) ── */ + if ( ! wp_style_is( 'bcb-code-box-inline', 'enqueued' ) ) { + $css = + '.bcb-code-box-wrapper{position:relative;display:block;width:100%;box-sizing:border-box;overflow:auto;padding:1em;border-radius:8px;max-height:var(--bcb-max-height,400px)}' + .'.bcb-code-box-wrapper.is-full{max-height:none;overflow:visible}' + .'.bcb-code-box-wrapper pre{margin:0;width:100%;box-sizing:border-box;white-space:pre-wrap;word-break:break-word;overflow-wrap:anywhere}' + .'.bcb-code-box-wrapper code{font-family:ui-monospace,Menlo,Monaco,Consolas,"Liberation Mono",monospace;font-size:var(--bcb-font-size,14px);display:block;max-width:100%;white-space:pre-wrap;word-break:break-word;overflow-wrap:anywhere}' + .'.bcb-code-box-wrapper .copy-btn{position:absolute;top:var(--bcb-btn-top,8px);right:var(--bcb-btn-offset-x,8px);left:auto;background:var(--bcb-btn-bg,transparent);color:var(--bcb-btn-color,#444);border:1px solid var(--bcb-btn-border-color,#444);padding:4px 10px;border-radius:4px;cursor:pointer!important;font-size:var(--bcb-btn-font-size,12px);z-index:9999;pointer-events:auto}' + .'.bcb-code-box-wrapper.copy-btn-left .copy-btn{right:auto;left:var(--bcb-btn-offset-x,8px)}' + .'.bcb-code-box-wrapper .copy-btn:hover{background:var(--bcb-btn-bg-hover,transparent);border-color:var(--bcb-btn-border-color-hover,#666);color:var(--bcb-btn-color-hover,#666)}' + .'.bcb-code-box-wrapper .filename{position:absolute;top:8px;left:8px;background:rgba(0,0,0,0.1);color:#666;padding:2px 8px;border-radius:4px;font-size:11px;font-family:ui-monospace,Menlo,Monaco,Consolas,"Liberation Mono",monospace;z-index:1}' + .'.bcb-code-box-wrapper.has-filename .copy-btn{right:8px;top:8px}' + .'.bcb-code-box-wrapper.has-custom-bg,.bcb-code-box-wrapper.has-custom-bg pre[class*="language-"],.bcb-code-box-wrapper.has-custom-bg code[class*="language-"]{background:var(--bcb-bg-color,#f5f5f5)!important;background-image:none!important;box-shadow:none!important;border:none!important;outline:none!important}' + .'.bcb-code-box-wrapper.has-custom-bg.line-numbers .line-numbers-rows > span:before{background:transparent!important;border:none!important;box-shadow:none!important}' + .'.bcb-code-box-wrapper.has-custom-bg.line-numbers pre[class*="language-"]{padding-left:3.8em}'; + + wp_register_style( 'bcb-code-box-inline', false ); + wp_enqueue_style( 'bcb-code-box-inline' ); + wp_add_inline_style( 'bcb-code-box-inline', $css ); } - if ( ! wp_script_is('bcb-code-box-init', 'enqueued') ) { - $js = << + /* Watch changes */ if(window.MutationObserver){ - var observer=new MutationObserver(function(mutations){ - mutations.forEach(function(m){ + new MutationObserver(function(muts){ + muts.forEach(function(m){ if(m.type==='attributes'&&m.attributeName==='data-brx-theme'){applyDarkTheme();} }); - }); - observer.observe(document.documentElement,{attributes:true,attributeFilter:['data-brx-theme']}); + }).observe(document.documentElement,{attributes:true,attributeFilter:['data-brx-theme']}); } if(document.readyState==='loading'){ - document.addEventListener('DOMContentLoaded',function(){initAllCodeBoxes();applyDarkTheme();}); + document.addEventListener('DOMContentLoaded',function(){initAll();applyDarkTheme();}); }else{ - initAllCodeBoxes();applyDarkTheme(); + initAll();applyDarkTheme(); + } + if(window.bricksIsFrontend){ + document.addEventListener('bricks/frontend/init',function(){initAll();applyDarkTheme();}); } - if(window.bricksIsFrontend){document.addEventListener('bricks/frontend/init',function(){initAllCodeBoxes();applyDarkTheme();});} })(); JS; - wp_register_script('bcb-code-box-init', '', [], '1.0.3', true); - wp_enqueue_script('bcb-code-box-init'); - wp_add_inline_script('bcb-code-box-init', $js); + wp_register_script( 'bcb-code-box-init', '', [], '1.0.4', true ); + wp_enqueue_script( 'bcb-code-box-init' ); + wp_add_inline_script( 'bcb-code-box-init', $js ); } } + /* ── Render ──────────────────────────────────────────────────────── */ + public function render() { - $settings = $this->settings; - $code = isset($settings['code']) ? (string) $settings['code'] : ''; - $language = isset($settings['language']) ? sanitize_text_field($settings['language']) : 'markup'; - $lineNumbers= !empty($settings['line_numbers']); - $showCopy = isset($settings['show_copy']) ? (bool)$settings['show_copy'] : true; - $font_size = isset($settings['font_size']) ? max(8, min(32, intval($settings['font_size']))) : 14; - $max_height = isset($settings['max_height']) ? max(0, intval($settings['max_height'])) : 400; - $full_height = !empty($settings['full_height']); - $show_filename = !empty($settings['show_filename']); - $filename = isset($settings['filename']) ? sanitize_text_field($settings['filename']) : ''; - - $allowed_languages = ['markup', 'javascript', 'php', 'css', 'java', 'python', 'sql', 'bash']; - $language = in_array($language, $allowed_languages) ? $language : 'markup'; + $settings = $this->settings; + + $code = isset( $settings['code'] ) ? (string) $settings['code'] : ''; + $language = isset( $settings['language'] ) ? sanitize_text_field( $settings['language'] ) : 'markup'; + $lineNumbers = ! empty( $settings['line_numbers'] ); + $showCopy = isset( $settings['show_copy'] ) ? (bool) $settings['show_copy'] : true; + $font_size = isset( $settings['font_size'] ) ? max( 8, min( 32, intval( $settings['font_size'] ) ) ) : 14; + $max_height = isset( $settings['max_height'] ) ? max( 0, intval( $settings['max_height'] ) ) : 400; + $full_height = ! empty( $settings['full_height'] ); + $show_fn = ! empty( $settings['show_filename'] ); + $filename = isset( $settings['filename'] ) ? sanitize_text_field( $settings['filename'] ) : ''; + $allowed_languages = [ 'markup', 'javascript', 'php', 'css', 'java', 'python', 'sql', 'bash' ]; + $language = in_array( $language, $allowed_languages, true ) ? $language : 'markup'; + + /* Classes */ $root_classes = [ 'bcb-code-box-wrapper' ]; - if ( $lineNumbers ) { $root_classes[] = 'line-numbers'; } - if ( $show_filename && !empty($filename) ) { $root_classes[] = 'has-filename'; } - if ( $full_height ) { $root_classes[] = 'is-full'; } + if ( $lineNumbers ) { $root_classes[] = 'line-numbers'; } + if ( $show_fn && $filename !== '' ) { $root_classes[] = 'has-filename'; } + if ( $full_height ) { $root_classes[] = 'is-full'; } - $bg_color_raw = $settings['background_color'] ?? ''; - if ( is_array($bg_color_raw) ) { - $bg_color_raw = (string) reset($bg_color_raw); - } - $has_custom_bg = is_string($bg_color_raw) && $bg_color_raw !== ''; - $bg_color = $has_custom_bg ? sanitize_text_field($bg_color_raw) : ''; - if ( $has_custom_bg ) { - $root_classes[] = 'has-custom-bg'; - } + /* Custom background */ + $bg_raw = $settings['background_color'] ?? ''; + if ( is_array( $bg_raw ) ) { $bg_raw = (string) reset( $bg_raw ); } + $has_custom_bg = is_string( $bg_raw ) && $bg_raw !== ''; + $bg_color = $has_custom_bg ? sanitize_text_field( $bg_raw ) : ''; + if ( $has_custom_bg ) { $root_classes[] = 'has-custom-bg'; } - $copy_btn_position = isset($settings['copy_btn_position']) ? sanitize_text_field($settings['copy_btn_position']) : 'right'; - $copy_btn_offset_y = isset($settings['copy_btn_offset_y']) ? max(0, intval($settings['copy_btn_offset_y'])) : 8; - $copy_btn_offset_x = isset($settings['copy_btn_offset_x']) ? max(0, intval($settings['copy_btn_offset_x'])) : 8; - $copy_btn_font_size = isset($settings['copy_btn_font_size']) ? max(8, min(24, intval($settings['copy_btn_font_size']))) : 12; + /* Copy button */ + $btn_pos = isset( $settings['copy_btn_position'] ) ? sanitize_text_field( $settings['copy_btn_position'] ) : 'right'; + $btn_offset_y = isset( $settings['copy_btn_offset_y'] ) ? max( 0, intval( $settings['copy_btn_offset_y'] ) ) : 8; + $btn_offset_x = isset( $settings['copy_btn_offset_x'] ) ? max( 0, intval( $settings['copy_btn_offset_x'] ) ) : 8; + $btn_font_size = isset( $settings['copy_btn_font_size'] ) ? max( 8, min( 24, intval( $settings['copy_btn_font_size'] ) ) ) : 12; - $copy_btn_bg_raw = $settings['copy_btn_bg'] ?? ''; - $copy_btn_color_raw = $settings['copy_btn_color'] ?? ''; - $copy_btn_border_raw= $settings['copy_btn_border_color'] ?? ''; + $btn_bg_raw = $settings['copy_btn_bg'] ?? ''; + $btn_col_raw = $settings['copy_btn_color'] ?? ''; + $btn_brd_raw = $settings['copy_btn_border_color'] ?? ''; - $copy_btn_bg = is_array($copy_btn_bg_raw) ? (string) reset($copy_btn_bg_raw) : (string) $copy_btn_bg_raw; - $copy_btn_color = is_array($copy_btn_color_raw) ? (string) reset($copy_btn_color_raw) : (string) $copy_btn_color_raw; - $copy_btn_border = is_array($copy_btn_border_raw) ? (string) reset($copy_btn_border_raw) : (string) $copy_btn_border_raw; + $btn_bg = is_array( $btn_bg_raw ) ? (string) reset( $btn_bg_raw ) : (string) $btn_bg_raw; + $btn_col = is_array( $btn_col_raw ) ? (string) reset( $btn_col_raw ) : (string) $btn_col_raw; + $btn_brd = is_array( $btn_brd_raw ) ? (string) reset( $btn_brd_raw ) : (string) $btn_brd_raw; - if ( $copy_btn_position === 'left' ) { - $root_classes[] = 'copy-btn-left'; - } + if ( $btn_pos === 'left' ) { $root_classes[] = 'copy-btn-left'; } - $style_attr = '--bcb-font-size: ' . esc_attr($font_size) . 'px; --bcb-max-height: ' . esc_attr($max_height) . 'px;'; - $style_attr .= ' --bcb-btn-top: ' . esc_attr($copy_btn_offset_y) . 'px;'; - $style_attr .= ' --bcb-btn-offset-x: ' . esc_attr($copy_btn_offset_x) . 'px;'; - $style_attr .= ' --bcb-btn-font-size: ' . esc_attr($copy_btn_font_size) . 'px;'; - if ( $copy_btn_bg !== '' ) { - $style_attr .= ' --bcb-btn-bg: ' . esc_attr($copy_btn_bg) . ';'; - $style_attr .= ' --bcb-btn-bg-hover: ' . esc_attr($copy_btn_bg) . ';'; - } - if ( $copy_btn_color !== '' ) { - $style_attr .= ' --bcb-btn-color: ' . esc_attr($copy_btn_color) . ';'; - $style_attr .= ' --bcb-btn-color-hover: ' . esc_attr($copy_btn_color) . ';'; - } - if ( $copy_btn_border !== '' ) { - $style_attr .= ' --bcb-btn-border-color: ' . esc_attr($copy_btn_border) . ';'; - $style_attr .= ' --bcb-btn-border-color-hover: ' . esc_attr($copy_btn_border) . ';'; - } - if ( $has_custom_bg ) { - $style_attr .= ' --bcb-bg-color: ' . esc_attr($bg_color) . ';'; - } + /* Inline style (CSS custom properties) */ + $style = '--bcb-font-size:' . esc_attr( $font_size ) . 'px;'; + $style .= '--bcb-max-height:' . esc_attr( $max_height ) . 'px;'; + $style .= '--bcb-btn-top:' . esc_attr( $btn_offset_y ) . 'px;'; + $style .= '--bcb-btn-offset-x:'. esc_attr( $btn_offset_x ) . 'px;'; + $style .= '--bcb-btn-font-size:'. esc_attr( $btn_font_size ) . 'px;'; + if ( $btn_bg !== '' ) { $style .= '--bcb-btn-bg:' . esc_attr( $btn_bg ) . ';--bcb-btn-bg-hover:' . esc_attr( $btn_bg ) . ';'; } + if ( $btn_col !== '' ) { $style .= '--bcb-btn-color:' . esc_attr( $btn_col ) . ';--bcb-btn-color-hover:' . esc_attr( $btn_col ) . ';'; } + if ( $btn_brd !== '' ) { $style .= '--bcb-btn-border-color:'. esc_attr( $btn_brd ) . ';--bcb-btn-border-color-hover:' . esc_attr( $btn_brd ) . ';'; } + if ( $has_custom_bg ) { $style .= '--bcb-bg-color:' . esc_attr( $bg_color ) . ';'; } + + /* Root attributes */ + $this->set_attribute( '_root', 'class', $root_classes ); + $this->set_attribute( '_root', 'style', $style ); + $this->set_attribute( '_root', 'data-lang', esc_attr( $language ) ); + $this->set_attribute( '_root', 'data-copy', $showCopy ? '1' : '0' ); + $this->set_attribute( '_root', 'data-label-de', esc_attr( sanitize_text_field( $settings['label_copy_de'] ?? 'πŸ“‹ Kopieren' ) ) ); + $this->set_attribute( '_root', 'data-done-de', esc_attr( sanitize_text_field( $settings['label_done_de'] ?? 'βœ… Kopiert!' ) ) ); + $this->set_attribute( '_root', 'data-label-en', esc_attr( sanitize_text_field( $settings['label_copy_en'] ?? 'πŸ“‹ Copy' ) ) ); + $this->set_attribute( '_root', 'data-done-en', esc_attr( sanitize_text_field( $settings['label_done_en'] ?? 'βœ… Copied!' ) ) ); - $this->set_attribute('_root', 'class', $root_classes); - $this->set_attribute('_root', 'style', $style_attr); - $this->set_attribute('_root', 'data-lang', esc_attr($language)); - $this->set_attribute('_root', 'data-copy', $showCopy ? '1' : '0'); - $this->set_attribute('_root', 'data-label-de', esc_attr(sanitize_text_field($settings['label_copy_de'] ?? 'πŸ“‹ Kopieren'))); - $this->set_attribute('_root', 'data-done-de', esc_attr(sanitize_text_field($settings['label_done_de'] ?? 'βœ… Kopiert!'))); - $this->set_attribute('_root', 'data-label-en', esc_attr(sanitize_text_field($settings['label_copy_en'] ?? 'πŸ“‹ Copy'))); - $this->set_attribute('_root', 'data-done-en', esc_attr(sanitize_text_field($settings['label_done_en'] ?? 'βœ… Copied!'))); - - echo '
render_attributes('_root') . '>'; - if ( $show_filename && !empty($filename) ) { - echo '
' . esc_html($filename) . '
'; + /* Output */ + echo '
render_attributes( '_root' ) . '>'; + + if ( $show_fn && $filename !== '' ) { + echo '
' . esc_html( $filename ) . '
'; } + if ( $showCopy ) { - $locale = function_exists('determine_locale') ? determine_locale() : get_locale(); - $is_en = is_string($locale) && strpos(strtolower($locale), 'en') === 0; + $locale = function_exists( 'determine_locale' ) ? determine_locale() : get_locale(); + $is_en = is_string( $locale ) && strpos( strtolower( $locale ), 'en' ) === 0; $initialLabel = $is_en - ? sanitize_text_field($settings['label_copy_en'] ?? 'πŸ“‹ Copy') - : sanitize_text_field($settings['label_copy_de'] ?? 'πŸ“‹ Kopieren'); - echo ''; + ? sanitize_text_field( $settings['label_copy_en'] ?? 'πŸ“‹ Copy' ) + : sanitize_text_field( $settings['label_copy_de'] ?? 'πŸ“‹ Kopieren' ); + echo ''; } - $pre_classes = $lineNumbers ? 'line-numbers' : ''; - echo '
' . esc_html($code) . '
'; - echo ''; + + echo '
'
+           . ''
+           . esc_html( $code )
+           . '
'; + + echo ''; + echo '
'; } } From 6284f8eee80f2ff2511a4e4fded5f4aaec04f1bd Mon Sep 17 00:00:00 2001 From: Ole Poetter <48469242+olpo24@users.noreply.github.com> Date: Thu, 21 May 2026 07:55:18 +0200 Subject: [PATCH 4/4] added the local prism.js assets --- assets/prism/prism-autoloader.min.js | 541 +++++ assets/prism/prism-line-numbers.min.css | 40 + assets/prism/prism-line-numbers.min.js | 252 +++ assets/prism/prism.min.js | 1946 +++++++++++++++++ assets/prism/themes/prism-coy.min.css | 219 ++ assets/prism/themes/prism-funky.min.css | 130 ++ assets/prism/themes/prism-okaidia.min.css | 123 ++ .../prism/themes/prism-solarizedlight.min.css | 150 ++ assets/prism/themes/prism-tomorrow.min.css | 122 ++ assets/prism/themes/prism-twilight.min.css | 169 ++ assets/prism/themes/prism.min.css | 140 ++ 11 files changed, 3832 insertions(+) create mode 100644 assets/prism/prism-autoloader.min.js create mode 100644 assets/prism/prism-line-numbers.min.css create mode 100644 assets/prism/prism-line-numbers.min.js create mode 100644 assets/prism/prism.min.js create mode 100644 assets/prism/themes/prism-coy.min.css create mode 100644 assets/prism/themes/prism-funky.min.css create mode 100644 assets/prism/themes/prism-okaidia.min.css create mode 100644 assets/prism/themes/prism-solarizedlight.min.css create mode 100644 assets/prism/themes/prism-tomorrow.min.css create mode 100644 assets/prism/themes/prism-twilight.min.css create mode 100644 assets/prism/themes/prism.min.css diff --git a/assets/prism/prism-autoloader.min.js b/assets/prism/prism-autoloader.min.js new file mode 100644 index 0000000..2881b2d --- /dev/null +++ b/assets/prism/prism-autoloader.min.js @@ -0,0 +1,541 @@ +(function () { + + if (typeof Prism === 'undefined' || typeof document === 'undefined') { + return; + } + + /* eslint-disable */ + + /** + * The dependencies map is built automatically with gulp. + * + * @type {Object} + */ + var lang_dependencies = /*dependencies_placeholder[*/{ + "javascript": "clike", + "actionscript": "javascript", + "apex": [ + "clike", + "sql" + ], + "arduino": "cpp", + "aspnet": [ + "markup", + "csharp" + ], + "birb": "clike", + "bison": "c", + "c": "clike", + "csharp": "clike", + "cpp": "c", + "cfscript": "clike", + "chaiscript": [ + "clike", + "cpp" + ], + "cilkc": "c", + "cilkcpp": "cpp", + "coffeescript": "javascript", + "crystal": "ruby", + "css-extras": "css", + "d": "clike", + "dart": "clike", + "django": "markup-templating", + "ejs": [ + "javascript", + "markup-templating" + ], + "etlua": [ + "lua", + "markup-templating" + ], + "erb": [ + "ruby", + "markup-templating" + ], + "fsharp": "clike", + "firestore-security-rules": "clike", + "flow": "javascript", + "ftl": "markup-templating", + "gml": "clike", + "glsl": "c", + "go": "clike", + "gradle": "clike", + "groovy": "clike", + "haml": "ruby", + "handlebars": "markup-templating", + "haxe": "clike", + "hlsl": "c", + "idris": "haskell", + "java": "clike", + "javadoc": [ + "markup", + "java", + "javadoclike" + ], + "jolie": "clike", + "jsdoc": [ + "javascript", + "javadoclike", + "typescript" + ], + "js-extras": "javascript", + "json5": "json", + "jsonp": "json", + "js-templates": "javascript", + "kotlin": "clike", + "latte": [ + "clike", + "markup-templating", + "php" + ], + "less": "css", + "lilypond": "scheme", + "liquid": "markup-templating", + "markdown": "markup", + "markup-templating": "markup", + "mongodb": "javascript", + "n4js": "javascript", + "objectivec": "c", + "opencl": "c", + "parser": "markup", + "php": "markup-templating", + "phpdoc": [ + "php", + "javadoclike" + ], + "php-extras": "php", + "plsql": "sql", + "processing": "clike", + "protobuf": "clike", + "pug": [ + "markup", + "javascript" + ], + "purebasic": "clike", + "purescript": "haskell", + "qsharp": "clike", + "qml": "javascript", + "qore": "clike", + "racket": "scheme", + "cshtml": [ + "markup", + "csharp" + ], + "jsx": [ + "markup", + "javascript" + ], + "tsx": [ + "jsx", + "typescript" + ], + "reason": "clike", + "ruby": "clike", + "sass": "css", + "scss": "css", + "scala": "java", + "shell-session": "bash", + "smarty": "markup-templating", + "solidity": "clike", + "soy": "markup-templating", + "sparql": "turtle", + "sqf": "clike", + "squirrel": "clike", + "stata": [ + "mata", + "java", + "python" + ], + "t4-cs": [ + "t4-templating", + "csharp" + ], + "t4-vb": [ + "t4-templating", + "vbnet" + ], + "tap": "yaml", + "tt2": [ + "clike", + "markup-templating" + ], + "textile": "markup", + "twig": "markup-templating", + "typescript": "javascript", + "v": "clike", + "vala": "clike", + "vbnet": "basic", + "velocity": "markup", + "wiki": "markup", + "xeora": "markup", + "xml-doc": "markup", + "xquery": "markup" + }/*]*/; + + var lang_aliases = /*aliases_placeholder[*/{ + "html": "markup", + "xml": "markup", + "svg": "markup", + "mathml": "markup", + "ssml": "markup", + "atom": "markup", + "rss": "markup", + "js": "javascript", + "g4": "antlr4", + "ino": "arduino", + "arm-asm": "armasm", + "art": "arturo", + "adoc": "asciidoc", + "avs": "avisynth", + "avdl": "avro-idl", + "gawk": "awk", + "sh": "bash", + "shell": "bash", + "shortcode": "bbcode", + "rbnf": "bnf", + "oscript": "bsl", + "cs": "csharp", + "dotnet": "csharp", + "cfc": "cfscript", + "cilk-c": "cilkc", + "cilk-cpp": "cilkcpp", + "cilk": "cilkcpp", + "coffee": "coffeescript", + "conc": "concurnas", + "jinja2": "django", + "dns-zone": "dns-zone-file", + "dockerfile": "docker", + "gv": "dot", + "eta": "ejs", + "xlsx": "excel-formula", + "xls": "excel-formula", + "gamemakerlanguage": "gml", + "po": "gettext", + "gni": "gn", + "ld": "linker-script", + "go-mod": "go-module", + "hbs": "handlebars", + "mustache": "handlebars", + "hs": "haskell", + "idr": "idris", + "gitignore": "ignore", + "hgignore": "ignore", + "npmignore": "ignore", + "webmanifest": "json", + "kt": "kotlin", + "kts": "kotlin", + "kum": "kumir", + "tex": "latex", + "context": "latex", + "ly": "lilypond", + "emacs": "lisp", + "elisp": "lisp", + "emacs-lisp": "lisp", + "md": "markdown", + "moon": "moonscript", + "n4jsd": "n4js", + "nani": "naniscript", + "objc": "objectivec", + "qasm": "openqasm", + "objectpascal": "pascal", + "px": "pcaxis", + "pcode": "peoplecode", + "plantuml": "plant-uml", + "pq": "powerquery", + "mscript": "powerquery", + "pbfasm": "purebasic", + "purs": "purescript", + "py": "python", + "qs": "qsharp", + "rkt": "racket", + "razor": "cshtml", + "rpy": "renpy", + "res": "rescript", + "robot": "robotframework", + "rb": "ruby", + "sh-session": "shell-session", + "shellsession": "shell-session", + "smlnj": "sml", + "sol": "solidity", + "sln": "solution-file", + "rq": "sparql", + "sclang": "supercollider", + "t4": "t4-cs", + "trickle": "tremor", + "troy": "tremor", + "trig": "turtle", + "ts": "typescript", + "tsconfig": "typoscript", + "uscript": "unrealscript", + "uc": "unrealscript", + "url": "uri", + "vb": "visual-basic", + "vba": "visual-basic", + "webidl": "web-idl", + "mathematica": "wolfram", + "nb": "wolfram", + "wl": "wolfram", + "xeoracube": "xeora", + "yml": "yaml" + }/*]*/; + + /* eslint-enable */ + + /** + * @typedef LangDataItem + * @property {{ success?: () => void, error?: () => void }[]} callbacks + * @property {boolean} [error] + * @property {boolean} [loading] + */ + /** @type {Object} */ + var lang_data = {}; + + var ignored_language = 'none'; + var languages_path = 'components/'; + + var script = Prism.util.currentScript(); + if (script) { + var autoloaderFile = /\bplugins\/autoloader\/prism-autoloader\.(?:min\.)?js(?:\?[^\r\n/]*)?$/i; + var prismFile = /(^|\/)[\w-]+\.(?:min\.)?js(?:\?[^\r\n/]*)?$/i; + + var autoloaderPath = script.getAttribute('data-autoloader-path'); + if (autoloaderPath != null) { + // data-autoloader-path is set, so just use it + languages_path = autoloaderPath.trim().replace(/\/?$/, '/'); + } else { + var src = script.src; + if (autoloaderFile.test(src)) { + // the script is the original autoloader script in the usual Prism project structure + languages_path = src.replace(autoloaderFile, 'components/'); + } else if (prismFile.test(src)) { + // the script is part of a bundle like a custom prism.js from the download page + languages_path = src.replace(prismFile, '$1components/'); + } + } + } + + var config = Prism.plugins.autoloader = { + languages_path: languages_path, + use_minified: true, + loadLanguages: loadLanguages + }; + + + /** + * Lazily loads an external script. + * + * @param {string} src + * @param {() => void} [success] + * @param {() => void} [error] + */ + function addScript(src, success, error) { + var s = document.createElement('script'); + s.src = src; + s.async = true; + s.onload = function () { + document.body.removeChild(s); + success && success(); + }; + s.onerror = function () { + document.body.removeChild(s); + error && error(); + }; + document.body.appendChild(s); + } + + /** + * Returns all additional dependencies of the given element defined by the `data-dependencies` attribute. + * + * @param {Element} element + * @returns {string[]} + */ + function getDependencies(element) { + var deps = (element.getAttribute('data-dependencies') || '').trim(); + if (!deps) { + var parent = element.parentElement; + if (parent && parent.tagName.toLowerCase() === 'pre') { + deps = (parent.getAttribute('data-dependencies') || '').trim(); + } + } + return deps ? deps.split(/\s*,\s*/g) : []; + } + + /** + * Returns whether the given language is currently loaded. + * + * @param {string} lang + * @returns {boolean} + */ + function isLoaded(lang) { + if (lang.indexOf('!') >= 0) { + // forced reload + return false; + } + + lang = lang_aliases[lang] || lang; // resolve alias + + if (lang in Prism.languages) { + // the given language is already loaded + return true; + } + + // this will catch extensions like CSS extras that don't add a grammar to Prism.languages + var data = lang_data[lang]; + return data && !data.error && data.loading === false; + } + + /** + * Returns the path to a grammar, using the language_path and use_minified config keys. + * + * @param {string} lang + * @returns {string} + */ + function getLanguagePath(lang) { + return config.languages_path + 'prism-' + lang + (config.use_minified ? '.min' : '') + '.js'; + } + + /** + * Loads all given grammars concurrently. + * + * @param {string[]|string} languages + * @param {(languages: string[]) => void} [success] + * @param {(language: string) => void} [error] This callback will be invoked on the first language to fail. + */ + function loadLanguages(languages, success, error) { + if (typeof languages === 'string') { + languages = [languages]; + } + + var total = languages.length; + var completed = 0; + var failed = false; + + if (total === 0) { + if (success) { + setTimeout(success, 0); + } + return; + } + + function successCallback() { + if (failed) { + return; + } + completed++; + if (completed === total) { + success && success(languages); + } + } + + languages.forEach(function (lang) { + loadLanguage(lang, successCallback, function () { + if (failed) { + return; + } + failed = true; + error && error(lang); + }); + }); + } + + /** + * Loads a grammar with its dependencies. + * + * @param {string} lang + * @param {() => void} [success] + * @param {() => void} [error] + */ + function loadLanguage(lang, success, error) { + var force = lang.indexOf('!') >= 0; + + lang = lang.replace('!', ''); + lang = lang_aliases[lang] || lang; + + function load() { + var data = lang_data[lang]; + if (!data) { + data = lang_data[lang] = { + callbacks: [] + }; + } + data.callbacks.push({ + success: success, + error: error + }); + + if (!force && isLoaded(lang)) { + // the language is already loaded and we aren't forced to reload + languageCallback(lang, 'success'); + } else if (!force && data.error) { + // the language failed to load before and we don't reload + languageCallback(lang, 'error'); + } else if (force || !data.loading) { + // the language isn't currently loading and/or we are forced to reload + data.loading = true; + data.error = false; + + addScript(getLanguagePath(lang), function () { + data.loading = false; + languageCallback(lang, 'success'); + + }, function () { + data.loading = false; + data.error = true; + languageCallback(lang, 'error'); + }); + } + } + + var dependencies = lang_dependencies[lang]; + if (dependencies && dependencies.length) { + loadLanguages(dependencies, load, error); + } else { + load(); + } + } + + /** + * Runs all callbacks of the given type for the given language. + * + * @param {string} lang + * @param {"success" | "error"} type + */ + function languageCallback(lang, type) { + if (lang_data[lang]) { + var callbacks = lang_data[lang].callbacks; + for (var i = 0, l = callbacks.length; i < l; i++) { + var callback = callbacks[i][type]; + if (callback) { + setTimeout(callback, 0); + } + } + callbacks.length = 0; + } + } + + Prism.hooks.add('complete', function (env) { + var element = env.element; + var language = env.language; + if (!element || !language || language === ignored_language) { + return; + } + + var deps = getDependencies(element); + if (/^diff-./i.test(language)) { + // the "diff-xxxx" format is used by the Diff Highlight plugin + deps.push('diff'); + deps.push(language.substr('diff-'.length)); + } else { + deps.push(language); + } + + if (!deps.every(isLoaded)) { + // the language or some dependencies aren't loaded + loadLanguages(deps, function () { + Prism.highlightElement(element); + }); + } + }); + +}()); diff --git a/assets/prism/prism-line-numbers.min.css b/assets/prism/prism-line-numbers.min.css new file mode 100644 index 0000000..5770530 --- /dev/null +++ b/assets/prism/prism-line-numbers.min.css @@ -0,0 +1,40 @@ +pre[class*="language-"].line-numbers { + position: relative; + padding-left: 3.8em; + counter-reset: linenumber; +} + +pre[class*="language-"].line-numbers > code { + position: relative; + white-space: inherit; +} + +.line-numbers .line-numbers-rows { + position: absolute; + pointer-events: none; + top: 0; + font-size: 100%; + left: -3.8em; + width: 3em; /* works for line-numbers below 1000 lines */ + letter-spacing: -1px; + border-right: 1px solid #999; + + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + +} + + .line-numbers-rows > span { + display: block; + counter-increment: linenumber; + } + + .line-numbers-rows > span:before { + content: counter(linenumber); + color: #999; + display: block; + padding-right: 0.8em; + text-align: right; + } diff --git a/assets/prism/prism-line-numbers.min.js b/assets/prism/prism-line-numbers.min.js new file mode 100644 index 0000000..71e8b69 --- /dev/null +++ b/assets/prism/prism-line-numbers.min.js @@ -0,0 +1,252 @@ +(function () { + + if (typeof Prism === 'undefined' || typeof document === 'undefined') { + return; + } + + /** + * Plugin name which is used as a class name for
 which is activating the plugin
+	 *
+	 * @type {string}
+	 */
+	var PLUGIN_NAME = 'line-numbers';
+
+	/**
+	 * Regular expression used for determining line breaks
+	 *
+	 * @type {RegExp}
+	 */
+	var NEW_LINE_EXP = /\n(?!$)/g;
+
+
+	/**
+	 * Global exports
+	 */
+	var config = Prism.plugins.lineNumbers = {
+		/**
+		 * Get node for provided line number
+		 *
+		 * @param {Element} element pre element
+		 * @param {number} number line number
+		 * @returns {Element|undefined}
+		 */
+		getLine: function (element, number) {
+			if (element.tagName !== 'PRE' || !element.classList.contains(PLUGIN_NAME)) {
+				return;
+			}
+
+			var lineNumberRows = element.querySelector('.line-numbers-rows');
+			if (!lineNumberRows) {
+				return;
+			}
+			var lineNumberStart = parseInt(element.getAttribute('data-start'), 10) || 1;
+			var lineNumberEnd = lineNumberStart + (lineNumberRows.children.length - 1);
+
+			if (number < lineNumberStart) {
+				number = lineNumberStart;
+			}
+			if (number > lineNumberEnd) {
+				number = lineNumberEnd;
+			}
+
+			var lineIndex = number - lineNumberStart;
+
+			return lineNumberRows.children[lineIndex];
+		},
+
+		/**
+		 * Resizes the line numbers of the given element.
+		 *
+		 * This function will not add line numbers. It will only resize existing ones.
+		 *
+		 * @param {HTMLElement} element A `
` element with line numbers.
+		 * @returns {void}
+		 */
+		resize: function (element) {
+			resizeElements([element]);
+		},
+
+		/**
+		 * Whether the plugin can assume that the units font sizes and margins are not depended on the size of
+		 * the current viewport.
+		 *
+		 * Setting this to `true` will allow the plugin to do certain optimizations for better performance.
+		 *
+		 * Set this to `false` if you use any of the following CSS units: `vh`, `vw`, `vmin`, `vmax`.
+		 *
+		 * @type {boolean}
+		 */
+		assumeViewportIndependence: true
+	};
+
+	/**
+	 * Resizes the given elements.
+	 *
+	 * @param {HTMLElement[]} elements
+	 */
+	function resizeElements(elements) {
+		elements = elements.filter(function (e) {
+			var codeStyles = getStyles(e);
+			var whiteSpace = codeStyles['white-space'];
+			return whiteSpace === 'pre-wrap' || whiteSpace === 'pre-line';
+		});
+
+		if (elements.length == 0) {
+			return;
+		}
+
+		var infos = elements.map(function (element) {
+			var codeElement = element.querySelector('code');
+			var lineNumbersWrapper = element.querySelector('.line-numbers-rows');
+			if (!codeElement || !lineNumbersWrapper) {
+				return undefined;
+			}
+
+			/** @type {HTMLElement} */
+			var lineNumberSizer = element.querySelector('.line-numbers-sizer');
+			var codeLines = codeElement.textContent.split(NEW_LINE_EXP);
+
+			if (!lineNumberSizer) {
+				lineNumberSizer = document.createElement('span');
+				lineNumberSizer.className = 'line-numbers-sizer';
+
+				codeElement.appendChild(lineNumberSizer);
+			}
+
+			lineNumberSizer.innerHTML = '0';
+			lineNumberSizer.style.display = 'block';
+
+			var oneLinerHeight = lineNumberSizer.getBoundingClientRect().height;
+			lineNumberSizer.innerHTML = '';
+
+			return {
+				element: element,
+				lines: codeLines,
+				lineHeights: [],
+				oneLinerHeight: oneLinerHeight,
+				sizer: lineNumberSizer,
+			};
+		}).filter(Boolean);
+
+		infos.forEach(function (info) {
+			var lineNumberSizer = info.sizer;
+			var lines = info.lines;
+			var lineHeights = info.lineHeights;
+			var oneLinerHeight = info.oneLinerHeight;
+
+			lineHeights[lines.length - 1] = undefined;
+			lines.forEach(function (line, index) {
+				if (line && line.length > 1) {
+					var e = lineNumberSizer.appendChild(document.createElement('span'));
+					e.style.display = 'block';
+					e.textContent = line;
+				} else {
+					lineHeights[index] = oneLinerHeight;
+				}
+			});
+		});
+
+		infos.forEach(function (info) {
+			var lineNumberSizer = info.sizer;
+			var lineHeights = info.lineHeights;
+
+			var childIndex = 0;
+			for (var i = 0; i < lineHeights.length; i++) {
+				if (lineHeights[i] === undefined) {
+					lineHeights[i] = lineNumberSizer.children[childIndex++].getBoundingClientRect().height;
+				}
+			}
+		});
+
+		infos.forEach(function (info) {
+			var lineNumberSizer = info.sizer;
+			var wrapper = info.element.querySelector('.line-numbers-rows');
+
+			lineNumberSizer.style.display = 'none';
+			lineNumberSizer.innerHTML = '';
+
+			info.lineHeights.forEach(function (height, lineNumber) {
+				wrapper.children[lineNumber].style.height = height + 'px';
+			});
+		});
+	}
+
+	/**
+	 * Returns style declarations for the element
+	 *
+	 * @param {Element} element
+	 */
+	function getStyles(element) {
+		if (!element) {
+			return null;
+		}
+
+		return window.getComputedStyle ? getComputedStyle(element) : (element.currentStyle || null);
+	}
+
+	var lastWidth = undefined;
+	window.addEventListener('resize', function () {
+		if (config.assumeViewportIndependence && lastWidth === window.innerWidth) {
+			return;
+		}
+		lastWidth = window.innerWidth;
+
+		resizeElements(Array.prototype.slice.call(document.querySelectorAll('pre.' + PLUGIN_NAME)));
+	});
+
+	Prism.hooks.add('complete', function (env) {
+		if (!env.code) {
+			return;
+		}
+
+		var code = /** @type {Element} */ (env.element);
+		var pre = /** @type {HTMLElement} */ (code.parentNode);
+
+		// works only for  wrapped inside 
 (not inline)
+		if (!pre || !/pre/i.test(pre.nodeName)) {
+			return;
+		}
+
+		// Abort if line numbers already exists
+		if (code.querySelector('.line-numbers-rows')) {
+			return;
+		}
+
+		// only add line numbers if  or one of its ancestors has the `line-numbers` class
+		if (!Prism.util.isActive(code, PLUGIN_NAME)) {
+			return;
+		}
+
+		// Remove the class 'line-numbers' from the 
+		code.classList.remove(PLUGIN_NAME);
+		// Add the class 'line-numbers' to the 
+		pre.classList.add(PLUGIN_NAME);
+
+		var match = env.code.match(NEW_LINE_EXP);
+		var linesNum = match ? match.length + 1 : 1;
+		var lineNumbersWrapper;
+
+		var lines = new Array(linesNum + 1).join('');
+
+		lineNumbersWrapper = document.createElement('span');
+		lineNumbersWrapper.setAttribute('aria-hidden', 'true');
+		lineNumbersWrapper.className = 'line-numbers-rows';
+		lineNumbersWrapper.innerHTML = lines;
+
+		if (pre.hasAttribute('data-start')) {
+			pre.style.counterReset = 'linenumber ' + (parseInt(pre.getAttribute('data-start'), 10) - 1);
+		}
+
+		env.element.appendChild(lineNumbersWrapper);
+
+		resizeElements([pre]);
+
+		Prism.hooks.run('line-numbers', env);
+	});
+
+	Prism.hooks.add('line-numbers', function (env) {
+		env.plugins = env.plugins || {};
+		env.plugins.lineNumbers = true;
+	});
+
+}());
diff --git a/assets/prism/prism.min.js b/assets/prism/prism.min.js
new file mode 100644
index 0000000..4b9c9a0
--- /dev/null
+++ b/assets/prism/prism.min.js
@@ -0,0 +1,1946 @@
+
+/* **********************************************
+     Begin prism-core.js
+********************************************** */
+
+/// 
+
+var _self = (typeof window !== 'undefined')
+	? window   // if in browser
+	: (
+		(typeof WorkerGlobalScope !== 'undefined' && self instanceof WorkerGlobalScope)
+			? self // if in worker
+			: {}   // if in node js
+	);
+
+/**
+ * Prism: Lightweight, robust, elegant syntax highlighting
+ *
+ * @license MIT 
+ * @author Lea Verou 
+ * @namespace
+ * @public
+ */
+var Prism = (function (_self) {
+
+	// Private helper vars
+	var lang = /(?:^|\s)lang(?:uage)?-([\w-]+)(?=\s|$)/i;
+	var uniqueId = 0;
+
+	// The grammar object for plaintext
+	var plainTextGrammar = {};
+
+
+	var _ = {
+		/**
+		 * By default, Prism will attempt to highlight all code elements (by calling {@link Prism.highlightAll}) on the
+		 * current page after the page finished loading. This might be a problem if e.g. you wanted to asynchronously load
+		 * additional languages or plugins yourself.
+		 *
+		 * By setting this value to `true`, Prism will not automatically highlight all code elements on the page.
+		 *
+		 * You obviously have to change this value before the automatic highlighting started. To do this, you can add an
+		 * empty Prism object into the global scope before loading the Prism script like this:
+		 *
+		 * ```js
+		 * window.Prism = window.Prism || {};
+		 * Prism.manual = true;
+		 * // add a new