diff --git a/docs/userGuide/syntax/includes.md b/docs/userGuide/syntax/includes.md index 43c41ab6f3..bbb681aa33 100644 --- a/docs/userGuide/syntax/includes.md +++ b/docs/userGuide/syntax/includes.md @@ -55,6 +55,8 @@ When setting the `id` of a fragment, be careful not to clash with heading anchor </box> +<include src="panels.md#script_and_styles_warning"></include> + <include src="tip.md" boilerplate > <span id="tip_body"> The `<include>` mechanism can be used inside any MarkBind source file (even inside the _frontmatter_ section) but it will not work inside some _special_ files such as the `_markbind/variables.md`. diff --git a/docs/userGuide/syntax/panels.md b/docs/userGuide/syntax/panels.md index 485f52a67e..96638d3c0f 100644 --- a/docs/userGuide/syntax/panels.md +++ b/docs/userGuide/syntax/panels.md @@ -145,6 +145,17 @@ plain text ... </variable> </include> +<div id = "script_and_styles_warning"> +<box type = "warning" header = "#### Global Effects of the Script and Styles from the Imported Externals"> + +Importing external resources that contains `script` or `styles` can inadvertently take global effects on your MarkBind website. Due to hoisting during processing, imported scripts and stylesheets affect the entire page. This could potentially alter its appearance and behavior beyond the intended scope. + +For example, if a CSS file imported via such means styles headings to be red, this change will be reflected page-wide. + +To safeguard against unintended consequences, consider directly incorporating the code or customizing styles to target specific elements or classes not used universally. This approach grants more precise control over your website's presentation and reduces the risk of unexpected changes. +</box> +</div> + **If `popup-url` attribute is provided, a popup button will be shown. If clicked, it opens the specified url in a new window.** <include src="codeAndOutput.md" boilerplate > diff --git a/packages/cli/test/functional/test_site/expected/siteData.json b/packages/cli/test/functional/test_site/expected/siteData.json index fb517276f5..567c2ddca7 100644 --- a/packages/cli/test/functional/test_site/expected/siteData.json +++ b/packages/cli/test/functional/test_site/expected/siteData.json @@ -453,6 +453,15 @@ "title": "glyphicon & octicon icon in page, only glyphicon & octicon stylesheets should be loaded", "headings": {}, "headingKeywords": {} + }, + { + "src": "testSourceContainScript.md", + "title": "Test: If source contains script or css, when included, the script or css should be included", + "headings": { + "panel-with-src-that-contains-css-and-script-header": "Panel with src that contains css and script header", + "h1-text": "\n\n\n h1 text\n \n" + }, + "headingKeywords": {} } ] } diff --git a/packages/cli/test/functional/test_site/expected/testPanels/PanelSourceContainsScript._include_.html b/packages/cli/test/functional/test_site/expected/testPanels/PanelSourceContainsScript._include_.html new file mode 100644 index 0000000000..8807a80c64 --- /dev/null +++ b/packages/cli/test/functional/test_site/expected/testPanels/PanelSourceContainsScript._include_.html @@ -0,0 +1,6 @@ +<h1 id="h1-text"> + + + h1 text + <a class="fa fa-anchor" href="#h1-text" onclick="event.stopPropagation()"></a> +</h1> \ No newline at end of file diff --git a/packages/cli/test/functional/test_site/expected/testSourceContainScript.html b/packages/cli/test/functional/test_site/expected/testSourceContainScript.html new file mode 100644 index 0000000000..5101e884eb --- /dev/null +++ b/packages/cli/test/functional/test_site/expected/testSourceContainScript.html @@ -0,0 +1,289 @@ +<!DOCTYPE html> +<html> + +<head> + + <meta name="default-head-top"> + <script src="/test_site/headFiles/customScriptTop.js"></script> + + <meta charset="utf-8"> + <meta http-equiv="X-UA-Compatible" content="IE=edge"> + <meta name="generator" content="MarkBind 5.3.0"> + <meta name="viewport" content="width=device-width, initial-scale=1"> + <title>Test: If source contains script or css, when included, the script or css should be included</title> + <link rel="stylesheet" href="/test_site/markbind/css/bootstrap.min.css"> + <link rel="stylesheet" href="/test_site/markbind/fontawesome/css/all.min.css"> + <link rel="stylesheet" href="/test_site/markbind/glyphicons/css/bootstrap-glyphicons.min.css"> + <link rel="stylesheet" href="/test_site/markbind/css/codeblock-dark.min.css"> + <link rel="stylesheet" href="/test_site/markbind/css/markbind.min.css"> + <script src="/test_site/markbind/js/polyfill.min.js"></script> + <script src="/test_site/markbind/js/vue.min.js"></script> + <script src="/test_site/markbind/js/markbind.min.js"></script> + <script src="testSourceContainScript.page-vue-render.js"></script> + <link rel="stylesheet" href="/test_site/plugins/testMarkbindPlugin/testMarkbindPluginStylesheet.css"> + <link rel="stylesheet" href="/test_site/plugins/web3Form/web-3-form.css"> + <link rel="stylesheet" href="/test_site/plugins/markbind-plugin-anchors/markbind-plugin-anchors.css"> + <link rel="stylesheet" href="/test_site/plugins/markbind-plugin-tree/markbind-plugin-tree.css"> + + <meta name="default-head-bottom"> + <link rel="stylesheet" href="/test_site/stylesheets/styles.css"> + <script src="/test_site/headFiles/customScriptBottom.js"></script> + <link rel="icon" href="/test_site/favicon.png"> +</head> +<script> + const baseUrl = '/test_site' +</script> + +<body> + <div id="app"> + + <div> + <header> + <navbar type="dark" default-highlight-on="sibling-or-child"> + <template #brand><a href="/" title="Home" class="navbar-brand">MarkBind Test Site</a></template> + <li><a class="nav-link" href="/test_site/bugs/index.html">Open Bugs</a></li> + </navbar> + <div class="bg-info display-4 text-center text-white"> + <br> + Test Jumbotron<br> + <br> + </div> + </header> + <p><strong>Relative Link Test</strong> This is a relative Intra-Site link in a layout (see <a href="/test_site/index.html#heading-with-hidden-keyword">link</a>)</p> + </div> + <div id="flex-body"> + <overlay-source id="site-nav" tag-name="nav" to="site-nav"> + <div class="site-nav-top"> + <div class="fw-bold mb-2" style="font-size: 1.25rem;"> + <div> + <h2 id="default-layout">Default Layout<a class="fa fa-anchor" href="#default-layout" onclick="event.stopPropagation()"></a></h2> + </div> + </div> + </div> + <div class="nav-component slim-scroll"> + <div> + <site-nav> + <overlay-source class="site-nav-list site-nav-list-root" tag-name="ul" to="mb-site-nav"> + <li> + <div class="site-nav-default-list-item site-nav-list-item-0" onclick="handleSiteNavClick(this)"><a href="/test_site/index.html">Home 🏠</a></div> + </li> + <li> + <div class="site-nav-default-list-item site-nav-list-item-0" onclick="handleSiteNavClick(this)"><a href="/test_site/bugs/index.html">Open Bugs 🐛</a></div> + </li> + <li class="site-nav-custom-list-item site-nav-list-item-0"> + <h3 id="testing-site-nav">Testing Site-Nav<a class="fa fa-anchor" href="#testing-site-nav" onclick="event.stopPropagation()"></a></h3> + </li> + <li> + <div class="site-nav-default-list-item site-nav-list-item-0" onclick="handleSiteNavClick(this)"><strong>Dropdown </strong> <span aria-hidden="true" class="glyphicon glyphicon-search"></span> title ✏️ + <!-- Nested list items will be placed inside a Dropdown menu --> + + <div class="site-nav-dropdown-btn-container"><i class="site-nav-dropdown-btn-icon site-nav-rotate-icon" onclick="handleSiteNavClick(this.parentNode.parentNode, false); event.stopPropagation();"> + <span class="glyphicon glyphicon-menu-down" aria-hidden="true"></span> + </i></div> + </div> + <ul class="site-nav-dropdown-container site-nav-dropdown-container-open site-nav-list"> + <li> + <div class="site-nav-default-list-item site-nav-list-item-1" onclick="handleSiteNavClick(this)"><a href="https://www.google.com/">Dropdown link one</a></div> + </li> + <li> + <div class="site-nav-default-list-item site-nav-list-item-1" onclick="handleSiteNavClick(this)"><a href="https://www.google.com/">Html within site-nav <span style="color: red;">should</span> be displayed properly</a></div> + </li> + <li> + <div class="site-nav-default-list-item site-nav-list-item-1" onclick="handleSiteNavClick(this)">Nested Dropdown title 📐 + + <div class="site-nav-dropdown-btn-container"><i class="site-nav-dropdown-btn-icon" onclick="handleSiteNavClick(this.parentNode.parentNode, false); event.stopPropagation();"> + <span class="glyphicon glyphicon-menu-down" aria-hidden="true"></span> + </i></div> + </div> + <ul class="site-nav-dropdown-container site-nav-list"> + <li> + <div class="site-nav-default-list-item site-nav-list-item-2" onclick="handleSiteNavClick(this)"><a href="https://www.google.com/"><strong>Nested</strong> Dropdown link one</a></div> + </li> + <li> + <div class="site-nav-default-list-item site-nav-list-item-2" onclick="handleSiteNavClick(this)"><a href="https://www.google.com/"><strong>Nested</strong> Dropdown link two</a></div> + </li> + </ul> + </li> + <li> + <div class="site-nav-default-list-item site-nav-list-item-1" onclick="handleSiteNavClick(this)"><a href="https://www.google.com/">Dropdown link two</a></div> + </li> + </ul> + </li> + <li> + <div class="site-nav-default-list-item site-nav-list-item-0" onclick="handleSiteNavClick(this)"><a href="https://www.google.com/"><mark>Third Link</mark> 📋</a></div> + </li> + <li> + <div class="site-nav-default-list-item site-nav-list-item-0" onclick="handleSiteNavClick(this)">Filler text <a href="https://www.youtube.com/"><span aria-hidden="true" class="glyphicon glyphicon-facetime-video"></span> Youtube 📺</a> filler text + <!-- Using a link as a Dropdown title will not render a Dropdown menu --> + + <div class="site-nav-dropdown-btn-container"><i class="site-nav-dropdown-btn-icon" onclick="handleSiteNavClick(this.parentNode.parentNode, false); event.stopPropagation();"> + <span class="glyphicon glyphicon-menu-down" aria-hidden="true"></span> + </i></div> + </div> + <ul class="site-nav-dropdown-container site-nav-list"> + <li> + <div class="site-nav-default-list-item site-nav-list-item-1" onclick="handleSiteNavClick(this)"><a href="https://www.youtube.com/watch?v=dQw4w9WgXcQ">The answer to everything in the universe</a></div> + </li> + <li> + <div class="site-nav-default-list-item site-nav-list-item-1" onclick="handleSiteNavClick(this)"><mark>Dropdown title</mark> <span aria-hidden="true" class="glyphicon glyphicon-comment"></span> ✏️ + <!-- Dropdown menus in are still supported inside, as long as the title is not a link --> + + <div class="site-nav-dropdown-btn-container"><i class="site-nav-dropdown-btn-icon site-nav-rotate-icon" onclick="handleSiteNavClick(this.parentNode.parentNode, false); event.stopPropagation();"> + <span class="glyphicon glyphicon-menu-down" aria-hidden="true"></span> + </i></div> + </div> + <ul class="site-nav-dropdown-container site-nav-dropdown-container-open site-nav-list"> + <li> + <div class="site-nav-default-list-item site-nav-list-item-2" onclick="handleSiteNavClick(this)"><a href="https://www.google.com/"><strong>Nested</strong> Dropdown link one</a></div> + </li> + </ul> + </li> + </ul> + </li> + <li> + <div class="site-nav-default-list-item site-nav-list-item-0" onclick="handleSiteNavClick(this)">Really Long Dropdown Title Really Long Dropdown Title Really Long Dropdown Title Really Long Dropdown + + <div class="site-nav-dropdown-btn-container"><i class="site-nav-dropdown-btn-icon" onclick="handleSiteNavClick(this.parentNode.parentNode, false); event.stopPropagation();"> + <span class="glyphicon glyphicon-menu-down" aria-hidden="true"></span> + </i></div> + </div> + <ul class="site-nav-dropdown-container site-nav-list"> + <li class="site-nav-custom-list-item site-nav-list-item-1">Really Really Long Text Really Really Long Text Really Really Long Text Really Really Long Text Really Really Long Text Really Really Long Text Really Really Long Text Really Really Long Text Really Really Long Text Really Really Long Text Really Really Long Text Really Really Long Text Really Really Long Text Really Really Long Text Really Really Long Text Really Really Long Text Really Really Long Text Really Really Long Text Really Really Long Text Really Really Long Text Really Really Long Text Really Really Long Text Really Really Long Text Really Really Long Text Really Really Long Text Really Really Long Text Really Really Long Text Really Really Long Text Really Really Long Text Really Really Long Text Really Really Long Text</li> + <li> + <div class="site-nav-default-list-item site-nav-list-item-1" onclick="handleSiteNavClick(this)">Nested Dropdown Title + + <div class="site-nav-dropdown-btn-container"><i class="site-nav-dropdown-btn-icon" onclick="handleSiteNavClick(this.parentNode.parentNode, false); event.stopPropagation();"> + <span class="glyphicon glyphicon-menu-down" aria-hidden="true"></span> + </i></div> + </div> + <ul class="site-nav-dropdown-container site-nav-list"> + <li class="site-nav-custom-list-item site-nav-list-item-2">Hello Doge Hello Doge 🐶</li> + <li> + <div class="site-nav-default-list-item site-nav-list-item-2" onclick="handleSiteNavClick(this)"><a href="/test_site/index.html"><strong>NESTED LINK</strong> Home 🏠</a></div> + </li> + <li class="site-nav-custom-list-item site-nav-list-item-2">Text cut off from height limit Text cut off from height limit Text cut off from height limit Text cut off from height limit Text cut off from height limit Text cut off from height limit Text cut off from height limit Text cut off from height limit Text cut off from height limit Text cut off from height limit Text cut off from height limit Text cut off from height limit Text cut off from height limit Text cut off from height limit Text cut off from height limit Text cut off from height limit Text cut off from height limit Text cut off from height limit Text cut off from height limit Text cut off from height limit Text cut off from height limit</li> + </ul> + </li> + </ul> + </li> + <li> + <div class="site-nav-default-list-item site-nav-list-item-0" onclick="handleSiteNavClick(this)">Test line break in navigation layout + + <div class="site-nav-dropdown-btn-container"><i class="site-nav-dropdown-btn-icon" onclick="handleSiteNavClick(this.parentNode.parentNode, false); event.stopPropagation();"> + <span class="glyphicon glyphicon-menu-down" aria-hidden="true"></span> + </i></div> + </div> + <ul class="site-nav-dropdown-container site-nav-list"> + <li class="site-nav-custom-list-item site-nav-list-item-1">Nested line break text ✂️</li> + <li> + <div class="site-nav-default-list-item site-nav-list-item-1" onclick="handleSiteNavClick(this)"><a href="/test_site/index.html">Nested line break href</a> + + <div class="site-nav-dropdown-btn-container"><i class="site-nav-dropdown-btn-icon" onclick="handleSiteNavClick(this.parentNode.parentNode, false); event.stopPropagation();"> + <span class="glyphicon glyphicon-menu-down" aria-hidden="true"></span> + </i></div> + </div> + <ul class="site-nav-dropdown-container site-nav-list"> + <li class="site-nav-custom-list-item site-nav-list-item-2">Nested Nested line break text ✂️</li> + </ul> + </li> + <li> + <div class="site-nav-default-list-item site-nav-list-item-1" onclick="handleSiteNavClick(this)">Nested line break dropdown menu + + <div class="site-nav-dropdown-btn-container"><i class="site-nav-dropdown-btn-icon" onclick="handleSiteNavClick(this.parentNode.parentNode, false); event.stopPropagation();"> + <span class="glyphicon glyphicon-menu-down" aria-hidden="true"></span> + </i></div> + </div> + <ul class="site-nav-dropdown-container site-nav-list"> + <li class="site-nav-custom-list-item site-nav-list-item-2">Line break item 2 📘</li> + </ul> + </li> + </ul> + </li> + </overlay-source> + </site-nav> + </div> + </div> + </overlay-source> + <div id="content-wrapper"> + <breadcrumb></breadcrumb> + <panel src="/test_site/testPanels/PanelSourceContainsScript._include_.html" expanded panelId="panel-with-src-that-contains-css-and-script-header"><template #header> + <h2 id="panel-with-src-that-contains-css-and-script-header">Panel with src that contains css and script header<a class="fa fa-anchor" href="#panel-with-src-that-contains-css-and-script-header" onclick="event.stopPropagation()"></a></h2> + </template></panel> + </div> + <overlay-source id="page-nav" tag-name="nav" to="page-nav"> + <div class="nav-component slim-scroll"> + </div> + </overlay-source> + <scroll-top-button></scroll-top-button> + </div> + <div> + <footer> + <h1 id="heading-in-footer-should-not-be-indexed">Heading in footer should not be indexed<a class="fa fa-anchor" href="#heading-in-footer-should-not-be-indexed" onclick="event.stopPropagation()"></a></h1> + <div class="text-center"> + This is a dynamic height footer that supports markdown <span>😄</span>! + </div> + </footer> + </div> + </div> +</body> +<script> + // JavaScript code specific to this component + console.log("Inline script executed! 35"); +</script> +<style> + /* CSS styles specific to this component */ + h1 { + color: red; + } +</style> +<script src="/test_site/markbind/js/bootstrap-utility.min.js"></script> +<script> + MarkBind.setupWithSearch() +</script> +<script src="/test_site/plugins/testMarkbindPlugin/testMarkbindPluginScript.js"></script> +<script>alert("Inline plugin script loaded!")</script> + +<!-- Global site tag (gtag.js) - Google Analytics --> +<script async src="https://www.googletagmanager.com/gtag/js?id=TRACKING-ID"></script> +<script> + window.dataLayer = window.dataLayer || []; + function gtag(){dataLayer.push(arguments);} + gtag('js', new Date()); + + gtag('config', 'TRACKING-ID'); + </script> + +<script> + function submitForm(element) { + event.preventDefault(); + const formData = new FormData(element); + const formProps = Object.fromEntries(formData); + const json = JSON.stringify(formProps); + const submitButton = element.querySelector('button[type="submit"]'); + const submitButtonText = submitButton.innerText; + submitButton.innerText = 'Please wait...' + fetch('https://api.web3forms.com/submit', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'Accept': 'application/json' + }, + body: json, + }) + .then(async (response) => { + if (response.status == 200) { + alert('Form submitted! Thank you for your response'); + } else { + alert('Error submitting form! Please try again later.'); + } + }) + .catch(error => { + alert('Error submitting form! Please try again later.'); + }) + .finally(() => { + submitButton.innerText = submitButtonText; + }) + } + </script> + +</html> \ No newline at end of file diff --git a/packages/cli/test/functional/test_site/expected/testSourceContainScript.page-vue-render.js b/packages/cli/test/functional/test_site/expected/testSourceContainScript.page-vue-render.js new file mode 100644 index 0000000000..686d1b1d99 --- /dev/null +++ b/packages/cli/test/functional/test_site/expected/testSourceContainScript.page-vue-render.js @@ -0,0 +1,16 @@ + + var pageVueRenderFn = function anonymous( +) { +with(this){return _c('div',{attrs:{"id":"app"}},[_c('div',[_c('header',[_c('navbar',{attrs:{"type":"dark","default-highlight-on":"sibling-or-child"},scopedSlots:_u([{key:"brand",fn:function(){return [_c('a',{staticClass:"navbar-brand",attrs:{"href":"/","title":"Home"}},[_v("MarkBind Test Site")])]},proxy:true}])},[_v(" "),_c('li',[_c('a',{staticClass:"nav-link",attrs:{"href":"/test_site/bugs/index.html"}},[_v("Open Bugs")])])]),_v(" "),_m(0)],1),_v(" "),_m(1)]),_v(" "),_c('div',{attrs:{"id":"flex-body"}},[_c('overlay-source',{attrs:{"id":"site-nav","tag-name":"nav","to":"site-nav"}},[_c('div',{staticClass:"site-nav-top"},[_c('div',{staticClass:"fw-bold mb-2",staticStyle:{"font-size":"1.25rem"}},[_c('div',[_c('h2',{attrs:{"id":"default-layout"}},[_v("Default Layout"),_c('a',{staticClass:"fa fa-anchor",attrs:{"href":"#default-layout","onclick":"event.stopPropagation()"}})])])])]),_v(" "),_c('div',{staticClass:"nav-component slim-scroll"},[_c('div',[_c('site-nav',[_c('overlay-source',{staticClass:"site-nav-list site-nav-list-root",attrs:{"tag-name":"ul","to":"mb-site-nav"}},[_c('li',[_c('div',{staticClass:"site-nav-default-list-item site-nav-list-item-0",attrs:{"onclick":"handleSiteNavClick(this)"}},[_c('a',{attrs:{"href":"/test_site/index.html"}},[_v("Home 🏠")])])]),_v(" "),_c('li',[_c('div',{staticClass:"site-nav-default-list-item site-nav-list-item-0",attrs:{"onclick":"handleSiteNavClick(this)"}},[_c('a',{attrs:{"href":"/test_site/bugs/index.html"}},[_v("Open Bugs 🐛")])])]),_v(" "),_c('li',{staticClass:"site-nav-custom-list-item site-nav-list-item-0"},[_c('h3',{attrs:{"id":"testing-site-nav"}},[_v("Testing Site-Nav"),_c('a',{staticClass:"fa fa-anchor",attrs:{"href":"#testing-site-nav","onclick":"event.stopPropagation()"}})])]),_v(" "),_c('li',[_c('div',{staticClass:"site-nav-default-list-item site-nav-list-item-0",attrs:{"onclick":"handleSiteNavClick(this)"}},[_c('strong',[_v("Dropdown ")]),_v(" "),_c('span',{staticClass:"glyphicon glyphicon-search",attrs:{"aria-hidden":"true"}}),_v(" title ✏️ "),_v(" "),_c('div',{staticClass:"site-nav-dropdown-btn-container"},[_c('i',{staticClass:"site-nav-dropdown-btn-icon site-nav-rotate-icon",attrs:{"onclick":"handleSiteNavClick(this.parentNode.parentNode, false); event.stopPropagation();"}},[_c('span',{staticClass:"glyphicon glyphicon-menu-down",attrs:{"aria-hidden":"true"}})])])]),_c('ul',{staticClass:"site-nav-dropdown-container site-nav-dropdown-container-open site-nav-list"},[_c('li',[_c('div',{staticClass:"site-nav-default-list-item site-nav-list-item-1",attrs:{"onclick":"handleSiteNavClick(this)"}},[_c('a',{attrs:{"href":"https://www.google.com/"}},[_v("Dropdown link one")])])]),_v(" "),_c('li',[_c('div',{staticClass:"site-nav-default-list-item site-nav-list-item-1",attrs:{"onclick":"handleSiteNavClick(this)"}},[_c('a',{attrs:{"href":"https://www.google.com/"}},[_v("Html within site-nav "),_c('span',{staticStyle:{"color":"red"}},[_v("should")]),_v(" be displayed properly")])])]),_v(" "),_c('li',[_c('div',{staticClass:"site-nav-default-list-item site-nav-list-item-1",attrs:{"onclick":"handleSiteNavClick(this)"}},[_v("Nested Dropdown title 📐\n\n"),_c('div',{staticClass:"site-nav-dropdown-btn-container"},[_c('i',{staticClass:"site-nav-dropdown-btn-icon",attrs:{"onclick":"handleSiteNavClick(this.parentNode.parentNode, false); event.stopPropagation();"}},[_c('span',{staticClass:"glyphicon glyphicon-menu-down",attrs:{"aria-hidden":"true"}})])])]),_c('ul',{staticClass:"site-nav-dropdown-container site-nav-list"},[_c('li',[_c('div',{staticClass:"site-nav-default-list-item site-nav-list-item-2",attrs:{"onclick":"handleSiteNavClick(this)"}},[_c('a',{attrs:{"href":"https://www.google.com/"}},[_c('strong',[_v("Nested")]),_v(" Dropdown link one")])])]),_v(" "),_c('li',[_c('div',{staticClass:"site-nav-default-list-item site-nav-list-item-2",attrs:{"onclick":"handleSiteNavClick(this)"}},[_c('a',{attrs:{"href":"https://www.google.com/"}},[_c('strong',[_v("Nested")]),_v(" Dropdown link two")])])])])]),_v(" "),_c('li',[_c('div',{staticClass:"site-nav-default-list-item site-nav-list-item-1",attrs:{"onclick":"handleSiteNavClick(this)"}},[_c('a',{attrs:{"href":"https://www.google.com/"}},[_v("Dropdown link two")])])])])]),_v(" "),_c('li',[_c('div',{staticClass:"site-nav-default-list-item site-nav-list-item-0",attrs:{"onclick":"handleSiteNavClick(this)"}},[_c('a',{attrs:{"href":"https://www.google.com/"}},[_c('mark',[_v("Third Link")]),_v(" 📋")])])]),_v(" "),_c('li',[_c('div',{staticClass:"site-nav-default-list-item site-nav-list-item-0",attrs:{"onclick":"handleSiteNavClick(this)"}},[_v("Filler text "),_c('a',{attrs:{"href":"https://www.youtube.com/"}},[_c('span',{staticClass:"glyphicon glyphicon-facetime-video",attrs:{"aria-hidden":"true"}}),_v(" Youtube 📺")]),_v(" filler text"),_v(" "),_c('div',{staticClass:"site-nav-dropdown-btn-container"},[_c('i',{staticClass:"site-nav-dropdown-btn-icon",attrs:{"onclick":"handleSiteNavClick(this.parentNode.parentNode, false); event.stopPropagation();"}},[_c('span',{staticClass:"glyphicon glyphicon-menu-down",attrs:{"aria-hidden":"true"}})])])]),_c('ul',{staticClass:"site-nav-dropdown-container site-nav-list"},[_c('li',[_c('div',{staticClass:"site-nav-default-list-item site-nav-list-item-1",attrs:{"onclick":"handleSiteNavClick(this)"}},[_c('a',{attrs:{"href":"https://www.youtube.com/watch?v=dQw4w9WgXcQ"}},[_v("The answer to everything in the universe")])])]),_v(" "),_c('li',[_c('div',{staticClass:"site-nav-default-list-item site-nav-list-item-1",attrs:{"onclick":"handleSiteNavClick(this)"}},[_c('mark',[_v("Dropdown title")]),_v(" "),_c('span',{staticClass:"glyphicon glyphicon-comment",attrs:{"aria-hidden":"true"}}),_v(" ✏️ "),_v(" "),_c('div',{staticClass:"site-nav-dropdown-btn-container"},[_c('i',{staticClass:"site-nav-dropdown-btn-icon site-nav-rotate-icon",attrs:{"onclick":"handleSiteNavClick(this.parentNode.parentNode, false); event.stopPropagation();"}},[_c('span',{staticClass:"glyphicon glyphicon-menu-down",attrs:{"aria-hidden":"true"}})])])]),_c('ul',{staticClass:"site-nav-dropdown-container site-nav-dropdown-container-open site-nav-list"},[_c('li',[_c('div',{staticClass:"site-nav-default-list-item site-nav-list-item-2",attrs:{"onclick":"handleSiteNavClick(this)"}},[_c('a',{attrs:{"href":"https://www.google.com/"}},[_c('strong',[_v("Nested")]),_v(" Dropdown link one")])])])])])])]),_v(" "),_c('li',[_c('div',{staticClass:"site-nav-default-list-item site-nav-list-item-0",attrs:{"onclick":"handleSiteNavClick(this)"}},[_v("Really Long Dropdown Title Really Long Dropdown Title Really Long Dropdown Title Really Long Dropdown\n\n"),_c('div',{staticClass:"site-nav-dropdown-btn-container"},[_c('i',{staticClass:"site-nav-dropdown-btn-icon",attrs:{"onclick":"handleSiteNavClick(this.parentNode.parentNode, false); event.stopPropagation();"}},[_c('span',{staticClass:"glyphicon glyphicon-menu-down",attrs:{"aria-hidden":"true"}})])])]),_c('ul',{staticClass:"site-nav-dropdown-container site-nav-list"},[_c('li',{staticClass:"site-nav-custom-list-item site-nav-list-item-1"},[_v("Really Really Long Text Really Really Long Text Really Really Long Text Really Really Long Text Really Really Long Text Really Really Long Text Really Really Long Text Really Really Long Text Really Really Long Text Really Really Long Text Really Really Long Text Really Really Long Text Really Really Long Text Really Really Long Text Really Really Long Text Really Really Long Text Really Really Long Text Really Really Long Text Really Really Long Text Really Really Long Text Really Really Long Text Really Really Long Text Really Really Long Text Really Really Long Text Really Really Long Text Really Really Long Text Really Really Long Text Really Really Long Text Really Really Long Text Really Really Long Text Really Really Long Text")]),_v(" "),_c('li',[_c('div',{staticClass:"site-nav-default-list-item site-nav-list-item-1",attrs:{"onclick":"handleSiteNavClick(this)"}},[_v("Nested Dropdown Title\n\n"),_c('div',{staticClass:"site-nav-dropdown-btn-container"},[_c('i',{staticClass:"site-nav-dropdown-btn-icon",attrs:{"onclick":"handleSiteNavClick(this.parentNode.parentNode, false); event.stopPropagation();"}},[_c('span',{staticClass:"glyphicon glyphicon-menu-down",attrs:{"aria-hidden":"true"}})])])]),_c('ul',{staticClass:"site-nav-dropdown-container site-nav-list"},[_c('li',{staticClass:"site-nav-custom-list-item site-nav-list-item-2"},[_v("Hello Doge Hello Doge 🐶")]),_v(" "),_c('li',[_c('div',{staticClass:"site-nav-default-list-item site-nav-list-item-2",attrs:{"onclick":"handleSiteNavClick(this)"}},[_c('a',{attrs:{"href":"/test_site/index.html"}},[_c('strong',[_v("NESTED LINK")]),_v(" Home 🏠")])])]),_v(" "),_c('li',{staticClass:"site-nav-custom-list-item site-nav-list-item-2"},[_v("Text cut off from height limit Text cut off from height limit Text cut off from height limit Text cut off from height limit Text cut off from height limit Text cut off from height limit Text cut off from height limit Text cut off from height limit Text cut off from height limit Text cut off from height limit Text cut off from height limit Text cut off from height limit Text cut off from height limit Text cut off from height limit Text cut off from height limit Text cut off from height limit Text cut off from height limit Text cut off from height limit Text cut off from height limit Text cut off from height limit Text cut off from height limit")])])])])]),_v(" "),_c('li',[_c('div',{staticClass:"site-nav-default-list-item site-nav-list-item-0",attrs:{"onclick":"handleSiteNavClick(this)"}},[_v("Test line break in navigation layout\n\n"),_c('div',{staticClass:"site-nav-dropdown-btn-container"},[_c('i',{staticClass:"site-nav-dropdown-btn-icon",attrs:{"onclick":"handleSiteNavClick(this.parentNode.parentNode, false); event.stopPropagation();"}},[_c('span',{staticClass:"glyphicon glyphicon-menu-down",attrs:{"aria-hidden":"true"}})])])]),_c('ul',{staticClass:"site-nav-dropdown-container site-nav-list"},[_c('li',{staticClass:"site-nav-custom-list-item site-nav-list-item-1"},[_v("Nested line break text ✂️")]),_v(" "),_c('li',[_c('div',{staticClass:"site-nav-default-list-item site-nav-list-item-1",attrs:{"onclick":"handleSiteNavClick(this)"}},[_c('a',{attrs:{"href":"/test_site/index.html"}},[_v("Nested line break href")]),_v(" "),_c('div',{staticClass:"site-nav-dropdown-btn-container"},[_c('i',{staticClass:"site-nav-dropdown-btn-icon",attrs:{"onclick":"handleSiteNavClick(this.parentNode.parentNode, false); event.stopPropagation();"}},[_c('span',{staticClass:"glyphicon glyphicon-menu-down",attrs:{"aria-hidden":"true"}})])])]),_c('ul',{staticClass:"site-nav-dropdown-container site-nav-list"},[_c('li',{staticClass:"site-nav-custom-list-item site-nav-list-item-2"},[_v("Nested Nested line break text ✂️")])])]),_v(" "),_c('li',[_c('div',{staticClass:"site-nav-default-list-item site-nav-list-item-1",attrs:{"onclick":"handleSiteNavClick(this)"}},[_v("Nested line break dropdown menu\n\n"),_c('div',{staticClass:"site-nav-dropdown-btn-container"},[_c('i',{staticClass:"site-nav-dropdown-btn-icon",attrs:{"onclick":"handleSiteNavClick(this.parentNode.parentNode, false); event.stopPropagation();"}},[_c('span',{staticClass:"glyphicon glyphicon-menu-down",attrs:{"aria-hidden":"true"}})])])]),_c('ul',{staticClass:"site-nav-dropdown-container site-nav-list"},[_c('li',{staticClass:"site-nav-custom-list-item site-nav-list-item-2"},[_v("Line break item 2 📘")])])])])])])],1)],1)])]),_v(" "),_c('div',{attrs:{"id":"content-wrapper"}},[_c('breadcrumb'),_v(" "),_c('panel',{attrs:{"src":"/test_site/testPanels/PanelSourceContainsScript._include_.html","expanded":"","panelId":"panel-with-src-that-contains-css-and-script-header"},scopedSlots:_u([{key:"header",fn:function(){return [_c('h2',{attrs:{"id":"panel-with-src-that-contains-css-and-script-header"}},[_v("Panel with src that contains css and script header"),_c('a',{staticClass:"fa fa-anchor",attrs:{"href":"#panel-with-src-that-contains-css-and-script-header","onclick":"event.stopPropagation()"}})])]},proxy:true}])})],1),_v(" "),_c('overlay-source',{attrs:{"id":"page-nav","tag-name":"nav","to":"page-nav"}},[_c('div',{staticClass:"nav-component slim-scroll"})]),_v(" "),_c('scroll-top-button')],1),_v(" "),_m(2)])} +}; + var pageVueStaticRenderFns = [function anonymous( +) { +with(this){return _c('div',{staticClass:"bg-info display-4 text-center text-white"},[_c('br'),_v("\n Test Jumbotron"),_c('br'),_v(" "),_c('br')])} +},function anonymous( +) { +with(this){return _c('p',[_c('strong',[_v("Relative Link Test")]),_v(" This is a relative Intra-Site link in a layout (see "),_c('a',{attrs:{"href":"/test_site/index.html#heading-with-hidden-keyword"}},[_v("link")]),_v(")")])} +},function anonymous( +) { +with(this){return _c('div',[_c('footer',[_c('h1',{attrs:{"id":"heading-in-footer-should-not-be-indexed"}},[_v("Heading in footer should not be indexed"),_c('a',{staticClass:"fa fa-anchor",attrs:{"href":"#heading-in-footer-should-not-be-indexed","onclick":"event.stopPropagation()"}})]),_v(" "),_c('div',{staticClass:"text-center"},[_v("\n This is a dynamic height footer that supports markdown "),_c('span',[_v("😄")]),_v("!\n ")])])])} +}]; + \ No newline at end of file diff --git a/packages/cli/test/functional/test_site/site.json b/packages/cli/test/functional/test_site/site.json index 18710cd1f2..bde8c35414 100644 --- a/packages/cli/test/functional/test_site/site.json +++ b/packages/cli/test/functional/test_site/site.json @@ -193,6 +193,10 @@ { "src": "testIconsInSiteLayout.md", "title": "glyphicon & octicon icon in page, only glyphicon & octicon stylesheets should be loaded" + }, + { + "src": "testSourceContainScript.md", + "title": "Test: If source contains script or css, when included, the script or css should be included" } ], "pagesExclude": ["**/*-fragment.md"], diff --git a/packages/cli/test/functional/test_site/testPanels/PanelSourceContainsScript.md b/packages/cli/test/functional/test_site/testPanels/PanelSourceContainsScript.md new file mode 100644 index 0000000000..79a933e11c --- /dev/null +++ b/packages/cli/test/functional/test_site/testPanels/PanelSourceContainsScript.md @@ -0,0 +1,13 @@ +<h1> +<script> + // JavaScript code specific to this component + console.log("Inline script executed! 35"); +</script> +<style> + /* CSS styles specific to this component */ + h1 { + color: red; + } +</style> +h1 text +</h1> diff --git a/packages/cli/test/functional/test_site/testSourceContainScript.md b/packages/cli/test/functional/test_site/testSourceContainScript.md new file mode 100644 index 0000000000..57a2995537 --- /dev/null +++ b/packages/cli/test/functional/test_site/testSourceContainScript.md @@ -0,0 +1 @@ +<panel header="## Panel with src that contains css and script header" src="testPanels/PanelSourceContainsScript.md" expanded></panel> \ No newline at end of file diff --git a/packages/core/src/External/External.ts b/packages/core/src/External/External.ts index e6517f109e..6dae06e282 100644 --- a/packages/core/src/External/External.ts +++ b/packages/core/src/External/External.ts @@ -16,11 +16,13 @@ export class External { externalManager: ExternalManager; sourceFilePath: string; includedFiles: Set<string>; + userScriptsAndStyles: string[]; - constructor(em: ExternalManager, srcFilePath: string) { + constructor(em: ExternalManager, srcFilePath: string, userScriptsAndStyles: string[]) { this.externalManager = em; this.sourceFilePath = srcFilePath; this.includedFiles = new Set([srcFilePath]); + this.userScriptsAndStyles = userScriptsAndStyles; } /** @@ -42,7 +44,7 @@ export class External { const pageSources = new PageSources(); const docId = `ext-${fsUtil.removeExtension(path.basename(asIfAtFilePath))}`; const nodeProcessor = new NodeProcessor(fileConfig, pageSources, variableProcessor, - pluginManager, siteLinkManager, undefined, docId); + pluginManager, siteLinkManager, this.userScriptsAndStyles, docId); const nunjucksProcessed = variableProcessor.renderWithSiteVariables(this.sourceFilePath, pageSources); const mdHtmlProcessed = await nodeProcessor.process(this.sourceFilePath, nunjucksProcessed, @@ -57,7 +59,9 @@ export class External { pageSources.addAllToSet(this.includedFiles); - await this.externalManager.generateDependencies(pageSources.getDynamicIncludeSrc(), this.includedFiles); + await this.externalManager.generateDependencies(pageSources.getDynamicIncludeSrc(), + this.includedFiles, + this.userScriptsAndStyles); return this; } diff --git a/packages/core/src/External/ExternalManager.ts b/packages/core/src/External/ExternalManager.ts index 357e66b5b6..67c06ed179 100644 --- a/packages/core/src/External/ExternalManager.ts +++ b/packages/core/src/External/ExternalManager.ts @@ -42,7 +42,9 @@ export class ExternalManager { * @param {Set<string>} includedFiles * @return {Promise<unknown[]>} */ - async generateDependencies(dependencies: DynamicSrc[], includedFiles: Set<string>) { + async generateDependencies(dependencies: DynamicSrc[], + includedFiles: Set<string>, + userScriptsAndStyles: string[]) { const resolvingExternals: Promise<External>[] = []; _.uniqBy(dependencies, d => d.asIfTo).forEach((src) => { @@ -55,7 +57,7 @@ export class ExternalManager { const resultPathWithExternalExt = fsUtil.setExtension(resultPath, '._include_.html'); if (!(resultPathWithExternalExt in this.builtFiles)) { - const external = new External(this, src.to); + const external = new External(this, src.to, userScriptsAndStyles); this.builtFiles[resultPathWithExternalExt] = external.resolveDependency(src.asIfTo, resultPathWithExternalExt, this.config); diff --git a/packages/core/src/Page/index.ts b/packages/core/src/Page/index.ts index c6b8daaca4..bca627d137 100644 --- a/packages/core/src/Page/index.ts +++ b/packages/core/src/Page/index.ts @@ -544,7 +544,9 @@ export class Page { }; pageSources.addAllToSet(this.includedFiles); - await externalManager.generateDependencies(pageSources.getDynamicIncludeSrc(), this.includedFiles); + await externalManager.generateDependencies(pageSources.getDynamicIncludeSrc(), + this.includedFiles, + this.pageUserScriptsAndStyles); this.collectHeadingsAndKeywords(pageContent); diff --git a/packages/core/src/html/NodeProcessor.ts b/packages/core/src/html/NodeProcessor.ts index db688f507a..b933a28b42 100644 --- a/packages/core/src/html/NodeProcessor.ts +++ b/packages/core/src/html/NodeProcessor.ts @@ -78,7 +78,7 @@ export class NodeProcessor { private variableProcessor: VariableProcessor, private pluginManager: PluginManager, private siteLinkManager: SiteLinkManager, - private userScriptsAndStyles: string[] | undefined, + private userScriptsAndStyles: string[], docId = '', ) { this.markdownProcessor = new MarkdownProcessor(docId); diff --git a/packages/core/src/html/scriptAndStyleTagProcessor.ts b/packages/core/src/html/scriptAndStyleTagProcessor.ts index e57e6ea70a..899c390b11 100644 --- a/packages/core/src/html/scriptAndStyleTagProcessor.ts +++ b/packages/core/src/html/scriptAndStyleTagProcessor.ts @@ -11,13 +11,12 @@ import { DomElement } from 'htmlparser2'; * @param node from the dom traversal * @param userScriptsAndStyles to store scripts and style tags for hoisting */ -export function processScriptAndStyleTag(node: DomElement, userScriptsAndStyles: string[] | undefined) { +export function processScriptAndStyleTag(node: DomElement, userScriptsAndStyles: string[]) { // Do not process script/style tags that are meant to be inserted in head/bottom of HTML const isHeadOrBottom = node.parent && (node.parent.name === 'head-top' || node.parent.name === 'head-bottom' || node.parent.name === 'script-bottom'); - // Do not process script/style tags that are from External - const isExternal = userScriptsAndStyles === undefined; - if (isHeadOrBottom || isExternal) { + + if (isHeadOrBottom) { return; }