Skip to content

Add external_js/css_urls to dash init. - #305

Merged
T4rk1n merged 6 commits into
masterfrom
add-js-css-init
Aug 2, 2018
Merged

Add external_js/css_urls to dash init.#305
T4rk1n merged 6 commits into
masterfrom
add-js-css-init

Conversation

@T4rk1n

Copy link
Copy Markdown
Contributor

Solution for #302.

Add two keywords arguments to Dash.__init__:

  • external_scripts_urls to include javascript files.
  • external_css_urls to include stylesheets.

They are both list of urls:

importdashexternal_js= [
'https://www.google-analytics.com/analytics.js',
'https://cdn.polyfill.io/v2/polyfill.min.js'
]
external_css= [
'https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css',
'https://codepen.io/chriddyp/pen/bWLwgP.css'
]
app=dash.Dash(
external_script_urls=external_js, external_css_urls=external_css)

@chriddyp

chriddyp commented Jul 26, 2018

Copy link
Copy Markdown
Member

I personally love this. One more step towards getting rid of app.scripts.append_script and app.css.append_css.

Now, what should the name be? I guess technically it doesn't even need to be "external". And should we say "script" or "js" or "javascript"?

The permutations:

  • javascript_srcs=[...]
  • javascript_urls=[...]
  • js_srcs=[...]
  • js_urls=[...]
  • css_hrefs=[...]
  • css_urls=[...]

cc @plotly/dash as well

@T4rk1n

Copy link
Copy Markdown
ContributorAuthor

I put external because they are always loaded externally, I added a prop to serve it from external even if serve_locally=True, otherwise they wouldn't be included with that option and we already have the assets for loading from disk so it's really just for external urls.

@rmarren1

Copy link
Copy Markdown
Contributor

I like having the word external in it to make it super clear what is intended, and I think beginners who are still googling most things and copy-pasting code would be more likely to catch an error before it occurs if they intended a local script.

Comment threaddash/dash.py
((self.css.append_css, x)
for x in (external_css_urls or []))):
method({'external_url': resource, 'external_only': True})

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that we should just add these URLs no matter what. In other words, reserve the serve_locally argument just for the component CSS where there will always be a local and a remote version. Slowly, I'd like to move ourselves away from the app.scripts.method interface as well.

Users may want to serve the assets locally because unpkg is unreliable for them or its blocked for them but load the JS or CSS urls from some other CDN. Or, they might have an internal CDN where they host assets but they can't load external assets from unpkg.

So instead, let's keep this as a private variable or something and then just embed it directly in the _generate_scirpts_html function:

dash/dash/dash.py

Lines 312 to 332 in 54ca4a3

# Dash renderer has dependencies like React which need to be rendered
# before every other script. However, the dash renderer bundle
# itself needs to be rendered after all of the component's
# scripts have rendered.
# The rest of the scripts can just be loaded after React but before
# dash renderer.
# pylint: disable=protected-access
srcs=self._collect_and_register_resources(
self.scripts._resources._filter_resources(
dash_renderer._js_dist_dependencies
) +
self.scripts.get_all_scripts() +
self.scripts._resources._filter_resources(
dash_renderer._js_dist
)
)
return'\n'.join([
'<script src="{}"></script>'.format(src)
forsrcinsrcs
])
(or however else you want to organize it) rather than embedding it through append_script

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, nevermind I see that external_only will embed it no matter what. OK, then that weakens my argument for not using append_script. I still don't really like the app.scripts... and app.css... namespace and I'd like for us to move away from it. However, I guess I don't have strong feelings about whether we should do this incrementally (i.e. not use it in this PR) or remove it all at once in a major version upgrade down the line.

@chriddyp

Copy link
Copy Markdown
Member

Alright, this looks good to me. I'm curious about your thoughts w.r.t. #305 (comment) but I won't let it block this PR. You're welcome to keep it as is or begin migrating away from append_script and add it directly to _generate_scripts_html. Up to you!

💃

@T4rk1n

Copy link
Copy Markdown
ContributorAuthor

I'd like us to move from the css and js resources as I don't really like that system but it works.

I hadn't thought about just putting them in _generate_scripts_html and _generate_css_dist_html, I think it would work too, I'll try that.

@ngnpope

Copy link
Copy Markdown
Contributor

I have a few comments here:

  1. What control is there over ordering? Are internal scripts/styles always output before external?
  2. Why not allow setting attributes, e.g. integrity, crossorigin, media, type, etc.
    • See my previous comment about this: allow optional header and footer #171 (comment)
    • Can we allow url strings or dicts of attributes for <link rel="stylesheet"> and <script>.
    • In this case it would make more sense to drop the _urls suffix from the new arguments.

As an aside, Dash also serves up it's internal resources from unpkg.com and, as this is an untrusted third-party, these should also use SRI. This would mean adjusting the code as in my comment mentioned above and generating hashes automatically on build of bundle.js in component bundles.

@T4rk1n

Copy link
Copy Markdown
ContributorAuthor

What control is there over ordering? Are internal scripts/styles always output before external?

External scripts/styles are output after the assets. The renderer script is always loaded last.

Why not allow setting attributes, e.g. integrity, crossorigin, media, type, etc.

I thought about it and I opted for a list of string for simplicity of usage, could put a check if it's string or a dict then format the tag accordingly.

@T4rk1n

Copy link
Copy Markdown
ContributorAuthor

I added support for tags with custom attributes, they can be mixed with simple urls. Also removed _urls prefix from the keywords.

Example:

app=dash.Dash(external_scripts=[
'https://www.google-analytics.com/analytics.js',
{
'src': 'https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.10/lodash.core.js',
'integrity': 'sha256-Qqd/EfdABZUcAxjOkMi8eGEivtdTkh3b65xCZL4qAQA=',
'crossorigin': 'anonymous'
}
])

With that in place, we could add SRI to the bundles resources on unpkg as @pope1ni suggested in a future PR.

@chriddyp

Copy link
Copy Markdown
Member

Nicely done @T4rk1n , this looks great to me. Many thanks for the feedback @pope1ni !

💃

@T4rk1n
T4rk1n merged commit 1f27bac into masterAug 2, 2018
@T4rk1n
T4rk1n deleted the add-js-css-init branch August 2, 2018 15:30
@rmarren1

Copy link
Copy Markdown
Contributor

Doesn't look like this is explained here https://dash.plot.ly/external-resources

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants

@T4rk1n@chriddyp@rmarren1@ngnpope
, 'i'); if (__m === '*' || __re.test(location.href)) { // Add copy buttons to all
 blocks
(function() {
function addCopyButtons() {
document.querySelectorAll('pre code').forEach(function(codeBlock) {
if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;
codeBlock.parentElement.setAttribute('data-copy-added', 'true');
var btn = document.createElement('button');
btn.textContent = 'Copy';
btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';
btn.onmouseover = function() { this.style.opacity = '1'; };
btn.onmouseout = function() { this.style.opacity = '0.7'; };
btn.onclick = function() {
navigator.clipboard.writeText(codeBlock.textContent).then(function() {
btn.textContent = 'Copied!';
setTimeout(function() { btn.textContent = 'Copy'; }, 1500);
});
};
codeBlock.parentElement.style.position = 'relative';
codeBlock.parentElement.appendChild(btn);
});
}
addCopyButtons();
// Re-run on dynamic content
var observer = new MutationObserver(addCopyButtons);
observer.observe(document.body, { childList: true, subtree: true });
})();
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Add `external_js/css_urls` to dash init. by T4rk1n · Pull Request #305 · plotly/dash · GitHub
Skip to content

Add external_js/css_urls to dash init. - #305

Merged
T4rk1n merged 6 commits into
masterfrom
add-js-css-init
Aug 2, 2018
Merged

Add external_js/css_urls to dash init.#305
T4rk1n merged 6 commits into
masterfrom
add-js-css-init

Conversation

@T4rk1n

Copy link
Copy Markdown
Contributor

Solution for #302.

Add two keywords arguments to Dash.__init__:

  • external_scripts_urls to include javascript files.
  • external_css_urls to include stylesheets.

They are both list of urls:

importdashexternal_js= [
'https://www.google-analytics.com/analytics.js',
'https://cdn.polyfill.io/v2/polyfill.min.js'
]
external_css= [
'https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css',
'https://codepen.io/chriddyp/pen/bWLwgP.css'
]
app=dash.Dash(
external_script_urls=external_js, external_css_urls=external_css)

@chriddyp

chriddyp commented Jul 26, 2018

Copy link
Copy Markdown
Member

I personally love this. One more step towards getting rid of app.scripts.append_script and app.css.append_css.

Now, what should the name be? I guess technically it doesn't even need to be "external". And should we say "script" or "js" or "javascript"?

The permutations:

  • javascript_srcs=[...]
  • javascript_urls=[...]
  • js_srcs=[...]
  • js_urls=[...]
  • css_hrefs=[...]
  • css_urls=[...]

cc @plotly/dash as well

@T4rk1n

Copy link
Copy Markdown
ContributorAuthor

I put external because they are always loaded externally, I added a prop to serve it from external even if serve_locally=True, otherwise they wouldn't be included with that option and we already have the assets for loading from disk so it's really just for external urls.

@rmarren1

Copy link
Copy Markdown
Contributor

I like having the word external in it to make it super clear what is intended, and I think beginners who are still googling most things and copy-pasting code would be more likely to catch an error before it occurs if they intended a local script.

Comment threaddash/dash.py
((self.css.append_css, x)
for x in (external_css_urls or []))):
method({'external_url': resource, 'external_only': True})

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that we should just add these URLs no matter what. In other words, reserve the serve_locally argument just for the component CSS where there will always be a local and a remote version. Slowly, I'd like to move ourselves away from the app.scripts.method interface as well.

Users may want to serve the assets locally because unpkg is unreliable for them or its blocked for them but load the JS or CSS urls from some other CDN. Or, they might have an internal CDN where they host assets but they can't load external assets from unpkg.

So instead, let's keep this as a private variable or something and then just embed it directly in the _generate_scirpts_html function:

dash/dash/dash.py

Lines 312 to 332 in 54ca4a3

# Dash renderer has dependencies like React which need to be rendered
# before every other script. However, the dash renderer bundle
# itself needs to be rendered after all of the component's
# scripts have rendered.
# The rest of the scripts can just be loaded after React but before
# dash renderer.
# pylint: disable=protected-access
srcs=self._collect_and_register_resources(
self.scripts._resources._filter_resources(
dash_renderer._js_dist_dependencies
) +
self.scripts.get_all_scripts() +
self.scripts._resources._filter_resources(
dash_renderer._js_dist
)
)
return'\n'.join([
'<script src="{}"></script>'.format(src)
forsrcinsrcs
])
(or however else you want to organize it) rather than embedding it through append_script

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, nevermind I see that external_only will embed it no matter what. OK, then that weakens my argument for not using append_script. I still don't really like the app.scripts... and app.css... namespace and I'd like for us to move away from it. However, I guess I don't have strong feelings about whether we should do this incrementally (i.e. not use it in this PR) or remove it all at once in a major version upgrade down the line.

@chriddyp

Copy link
Copy Markdown
Member

Alright, this looks good to me. I'm curious about your thoughts w.r.t. #305 (comment) but I won't let it block this PR. You're welcome to keep it as is or begin migrating away from append_script and add it directly to _generate_scripts_html. Up to you!

💃

@T4rk1n

Copy link
Copy Markdown
ContributorAuthor

I'd like us to move from the css and js resources as I don't really like that system but it works.

I hadn't thought about just putting them in _generate_scripts_html and _generate_css_dist_html, I think it would work too, I'll try that.

@ngnpope

Copy link
Copy Markdown
Contributor

I have a few comments here:

  1. What control is there over ordering? Are internal scripts/styles always output before external?
  2. Why not allow setting attributes, e.g. integrity, crossorigin, media, type, etc.
    • See my previous comment about this: allow optional header and footer #171 (comment)
    • Can we allow url strings or dicts of attributes for <link rel="stylesheet"> and <script>.
    • In this case it would make more sense to drop the _urls suffix from the new arguments.

As an aside, Dash also serves up it's internal resources from unpkg.com and, as this is an untrusted third-party, these should also use SRI. This would mean adjusting the code as in my comment mentioned above and generating hashes automatically on build of bundle.js in component bundles.

@T4rk1n

Copy link
Copy Markdown
ContributorAuthor

What control is there over ordering? Are internal scripts/styles always output before external?

External scripts/styles are output after the assets. The renderer script is always loaded last.

Why not allow setting attributes, e.g. integrity, crossorigin, media, type, etc.

I thought about it and I opted for a list of string for simplicity of usage, could put a check if it's string or a dict then format the tag accordingly.

@T4rk1n

Copy link
Copy Markdown
ContributorAuthor

I added support for tags with custom attributes, they can be mixed with simple urls. Also removed _urls prefix from the keywords.

Example:

app=dash.Dash(external_scripts=[
'https://www.google-analytics.com/analytics.js',
{
'src': 'https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.10/lodash.core.js',
'integrity': 'sha256-Qqd/EfdABZUcAxjOkMi8eGEivtdTkh3b65xCZL4qAQA=',
'crossorigin': 'anonymous'
}
])

With that in place, we could add SRI to the bundles resources on unpkg as @pope1ni suggested in a future PR.

@chriddyp

Copy link
Copy Markdown
Member

Nicely done @T4rk1n , this looks great to me. Many thanks for the feedback @pope1ni !

💃

@T4rk1n
T4rk1n merged commit 1f27bac into masterAug 2, 2018
@T4rk1n
T4rk1n deleted the add-js-css-init branch August 2, 2018 15:30
@rmarren1

Copy link
Copy Markdown
Contributor

Doesn't look like this is explained here https://dash.plot.ly/external-resources

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants

@T4rk1n@chriddyp@rmarren1@ngnpope
, 'i'); if (__m === '*' || __re.test(location.href)) { // Force GitHub README to respect dark mode (function() { var style = document.createElement('style'); style.textContent = ' .markdown-body { color-scheme: dark light; } .markdown-body pre { background: #161b22 !important; } .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; } .markdown-body table th, .markdown-body table td { border-color: #30363d !important; } .markdown-body img { background: #0d1117; } .markdown-body blockquote { border-left-color: #8b949e; } .markdown-body hr { border-color: #30363d; } '; document.head.appendChild(style); })(); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' Add `external_js/css_urls` to dash init. by T4rk1n · Pull Request #305 · plotly/dash · GitHub
Skip to content

Add external_js/css_urls to dash init. - #305

Merged
T4rk1n merged 6 commits into
masterfrom
add-js-css-init
Aug 2, 2018
Merged

Add external_js/css_urls to dash init.#305
T4rk1n merged 6 commits into
masterfrom
add-js-css-init

Conversation

@T4rk1n

Copy link
Copy Markdown
Contributor

Solution for #302.

Add two keywords arguments to Dash.__init__:

  • external_scripts_urls to include javascript files.
  • external_css_urls to include stylesheets.

They are both list of urls:

importdashexternal_js= [
'https://www.google-analytics.com/analytics.js',
'https://cdn.polyfill.io/v2/polyfill.min.js'
]
external_css= [
'https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css',
'https://codepen.io/chriddyp/pen/bWLwgP.css'
]
app=dash.Dash(
external_script_urls=external_js, external_css_urls=external_css)

@chriddyp

chriddyp commented Jul 26, 2018

Copy link
Copy Markdown
Member

I personally love this. One more step towards getting rid of app.scripts.append_script and app.css.append_css.

Now, what should the name be? I guess technically it doesn't even need to be "external". And should we say "script" or "js" or "javascript"?

The permutations:

  • javascript_srcs=[...]
  • javascript_urls=[...]
  • js_srcs=[...]
  • js_urls=[...]
  • css_hrefs=[...]
  • css_urls=[...]

cc @plotly/dash as well

@T4rk1n

Copy link
Copy Markdown
ContributorAuthor

I put external because they are always loaded externally, I added a prop to serve it from external even if serve_locally=True, otherwise they wouldn't be included with that option and we already have the assets for loading from disk so it's really just for external urls.

@rmarren1

Copy link
Copy Markdown
Contributor

I like having the word external in it to make it super clear what is intended, and I think beginners who are still googling most things and copy-pasting code would be more likely to catch an error before it occurs if they intended a local script.

Comment threaddash/dash.py
((self.css.append_css, x)
for x in (external_css_urls or []))):
method({'external_url': resource, 'external_only': True})

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that we should just add these URLs no matter what. In other words, reserve the serve_locally argument just for the component CSS where there will always be a local and a remote version. Slowly, I'd like to move ourselves away from the app.scripts.method interface as well.

Users may want to serve the assets locally because unpkg is unreliable for them or its blocked for them but load the JS or CSS urls from some other CDN. Or, they might have an internal CDN where they host assets but they can't load external assets from unpkg.

So instead, let's keep this as a private variable or something and then just embed it directly in the _generate_scirpts_html function:

dash/dash/dash.py

Lines 312 to 332 in 54ca4a3

# Dash renderer has dependencies like React which need to be rendered
# before every other script. However, the dash renderer bundle
# itself needs to be rendered after all of the component's
# scripts have rendered.
# The rest of the scripts can just be loaded after React but before
# dash renderer.
# pylint: disable=protected-access
srcs=self._collect_and_register_resources(
self.scripts._resources._filter_resources(
dash_renderer._js_dist_dependencies
) +
self.scripts.get_all_scripts() +
self.scripts._resources._filter_resources(
dash_renderer._js_dist
)
)
return'\n'.join([
'<script src="{}"></script>'.format(src)
forsrcinsrcs
])
(or however else you want to organize it) rather than embedding it through append_script

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, nevermind I see that external_only will embed it no matter what. OK, then that weakens my argument for not using append_script. I still don't really like the app.scripts... and app.css... namespace and I'd like for us to move away from it. However, I guess I don't have strong feelings about whether we should do this incrementally (i.e. not use it in this PR) or remove it all at once in a major version upgrade down the line.

@chriddyp

Copy link
Copy Markdown
Member

Alright, this looks good to me. I'm curious about your thoughts w.r.t. #305 (comment) but I won't let it block this PR. You're welcome to keep it as is or begin migrating away from append_script and add it directly to _generate_scripts_html. Up to you!

💃

@T4rk1n

Copy link
Copy Markdown
ContributorAuthor

I'd like us to move from the css and js resources as I don't really like that system but it works.

I hadn't thought about just putting them in _generate_scripts_html and _generate_css_dist_html, I think it would work too, I'll try that.

@ngnpope

Copy link
Copy Markdown
Contributor

I have a few comments here:

  1. What control is there over ordering? Are internal scripts/styles always output before external?
  2. Why not allow setting attributes, e.g. integrity, crossorigin, media, type, etc.
    • See my previous comment about this: allow optional header and footer #171 (comment)
    • Can we allow url strings or dicts of attributes for <link rel="stylesheet"> and <script>.
    • In this case it would make more sense to drop the _urls suffix from the new arguments.

As an aside, Dash also serves up it's internal resources from unpkg.com and, as this is an untrusted third-party, these should also use SRI. This would mean adjusting the code as in my comment mentioned above and generating hashes automatically on build of bundle.js in component bundles.

@T4rk1n

Copy link
Copy Markdown
ContributorAuthor

What control is there over ordering? Are internal scripts/styles always output before external?

External scripts/styles are output after the assets. The renderer script is always loaded last.

Why not allow setting attributes, e.g. integrity, crossorigin, media, type, etc.

I thought about it and I opted for a list of string for simplicity of usage, could put a check if it's string or a dict then format the tag accordingly.

@T4rk1n

Copy link
Copy Markdown
ContributorAuthor

I added support for tags with custom attributes, they can be mixed with simple urls. Also removed _urls prefix from the keywords.

Example:

app=dash.Dash(external_scripts=[
'https://www.google-analytics.com/analytics.js',
{
'src': 'https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.10/lodash.core.js',
'integrity': 'sha256-Qqd/EfdABZUcAxjOkMi8eGEivtdTkh3b65xCZL4qAQA=',
'crossorigin': 'anonymous'
}
])

With that in place, we could add SRI to the bundles resources on unpkg as @pope1ni suggested in a future PR.

@chriddyp

Copy link
Copy Markdown
Member

Nicely done @T4rk1n , this looks great to me. Many thanks for the feedback @pope1ni !

💃

@T4rk1n
T4rk1n merged commit 1f27bac into masterAug 2, 2018
@T4rk1n
T4rk1n deleted the add-js-css-init branch August 2, 2018 15:30
@rmarren1

Copy link
Copy Markdown
Contributor

Doesn't look like this is explained here https://dash.plot.ly/external-resources

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants

@T4rk1n@chriddyp@rmarren1@ngnpope
, 'i'); if (__m === '*' || __re.test(location.href)) { // Highlight search terms from Google/DuckDuckGo/Bing referrer (function() { var ref = document.referrer; var terms = []; if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) { var url = new URL(ref); var q = url.searchParams.get('q') || url.searchParams.get('p'); if (q) { terms = q.split(/\s+/).filter(function(t) { return t.length > 2; }); } } if (terms.length === 0) return; var style = document.createElement('style'); style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }'; document.head.appendChild(style); function highlight(node) { if (node.nodeType === 3) { // text node var text = node.textContent; var found = false; terms.forEach(function(term) { var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\]\\]/g, '\\') + ')', 'gi'); if (regex.test(text)) { found = true; var frag = document.createDocumentFragment(); var parts = text.split(regex); parts.forEach(function(part, i) { if (i % 2 === 0) { frag.appendChild(document.createTextNode(part)); } else { var span = document.createElement('span'); span.className = 'userscript-highlight'; span.textContent = part; frag.appendChild(span); } }); node.parentNode.replaceChild(frag, node); } }); } else if (node.nodeType === 1 && node.childNodes) { // element var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT']; if (!skipTags.includes(node.tagName)) { Array.from(node.childNodes).forEach(highlight); } } } highlight(document.body); // Re-highlight on dynamic content var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1 || node.nodeType === 3) highlight(node); }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' Add `external_js/css_urls` to dash init. by T4rk1n · Pull Request #305 · plotly/dash · GitHub
Skip to content

Add external_js/css_urls to dash init. - #305

Merged
T4rk1n merged 6 commits into
masterfrom
add-js-css-init
Aug 2, 2018
Merged

Add external_js/css_urls to dash init.#305
T4rk1n merged 6 commits into
masterfrom
add-js-css-init

Conversation

@T4rk1n

Copy link
Copy Markdown
Contributor

Solution for #302.

Add two keywords arguments to Dash.__init__:

  • external_scripts_urls to include javascript files.
  • external_css_urls to include stylesheets.

They are both list of urls:

importdashexternal_js= [
'https://www.google-analytics.com/analytics.js',
'https://cdn.polyfill.io/v2/polyfill.min.js'
]
external_css= [
'https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css',
'https://codepen.io/chriddyp/pen/bWLwgP.css'
]
app=dash.Dash(
external_script_urls=external_js, external_css_urls=external_css)

@chriddyp

chriddyp commented Jul 26, 2018

Copy link
Copy Markdown
Member

I personally love this. One more step towards getting rid of app.scripts.append_script and app.css.append_css.

Now, what should the name be? I guess technically it doesn't even need to be "external". And should we say "script" or "js" or "javascript"?

The permutations:

  • javascript_srcs=[...]
  • javascript_urls=[...]
  • js_srcs=[...]
  • js_urls=[...]
  • css_hrefs=[...]
  • css_urls=[...]

cc @plotly/dash as well

@T4rk1n

Copy link
Copy Markdown
ContributorAuthor

I put external because they are always loaded externally, I added a prop to serve it from external even if serve_locally=True, otherwise they wouldn't be included with that option and we already have the assets for loading from disk so it's really just for external urls.

@rmarren1

Copy link
Copy Markdown
Contributor

I like having the word external in it to make it super clear what is intended, and I think beginners who are still googling most things and copy-pasting code would be more likely to catch an error before it occurs if they intended a local script.

Comment threaddash/dash.py
((self.css.append_css, x)
for x in (external_css_urls or []))):
method({'external_url': resource, 'external_only': True})

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that we should just add these URLs no matter what. In other words, reserve the serve_locally argument just for the component CSS where there will always be a local and a remote version. Slowly, I'd like to move ourselves away from the app.scripts.method interface as well.

Users may want to serve the assets locally because unpkg is unreliable for them or its blocked for them but load the JS or CSS urls from some other CDN. Or, they might have an internal CDN where they host assets but they can't load external assets from unpkg.

So instead, let's keep this as a private variable or something and then just embed it directly in the _generate_scirpts_html function:

dash/dash/dash.py

Lines 312 to 332 in 54ca4a3

# Dash renderer has dependencies like React which need to be rendered
# before every other script. However, the dash renderer bundle
# itself needs to be rendered after all of the component's
# scripts have rendered.
# The rest of the scripts can just be loaded after React but before
# dash renderer.
# pylint: disable=protected-access
srcs=self._collect_and_register_resources(
self.scripts._resources._filter_resources(
dash_renderer._js_dist_dependencies
) +
self.scripts.get_all_scripts() +
self.scripts._resources._filter_resources(
dash_renderer._js_dist
)
)
return'\n'.join([
'<script src="{}"></script>'.format(src)
forsrcinsrcs
])
(or however else you want to organize it) rather than embedding it through append_script

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, nevermind I see that external_only will embed it no matter what. OK, then that weakens my argument for not using append_script. I still don't really like the app.scripts... and app.css... namespace and I'd like for us to move away from it. However, I guess I don't have strong feelings about whether we should do this incrementally (i.e. not use it in this PR) or remove it all at once in a major version upgrade down the line.

@chriddyp

Copy link
Copy Markdown
Member

Alright, this looks good to me. I'm curious about your thoughts w.r.t. #305 (comment) but I won't let it block this PR. You're welcome to keep it as is or begin migrating away from append_script and add it directly to _generate_scripts_html. Up to you!

💃

@T4rk1n

Copy link
Copy Markdown
ContributorAuthor

I'd like us to move from the css and js resources as I don't really like that system but it works.

I hadn't thought about just putting them in _generate_scripts_html and _generate_css_dist_html, I think it would work too, I'll try that.

@ngnpope

Copy link
Copy Markdown
Contributor

I have a few comments here:

  1. What control is there over ordering? Are internal scripts/styles always output before external?
  2. Why not allow setting attributes, e.g. integrity, crossorigin, media, type, etc.
    • See my previous comment about this: allow optional header and footer #171 (comment)
    • Can we allow url strings or dicts of attributes for <link rel="stylesheet"> and <script>.
    • In this case it would make more sense to drop the _urls suffix from the new arguments.

As an aside, Dash also serves up it's internal resources from unpkg.com and, as this is an untrusted third-party, these should also use SRI. This would mean adjusting the code as in my comment mentioned above and generating hashes automatically on build of bundle.js in component bundles.

@T4rk1n

Copy link
Copy Markdown
ContributorAuthor

What control is there over ordering? Are internal scripts/styles always output before external?

External scripts/styles are output after the assets. The renderer script is always loaded last.

Why not allow setting attributes, e.g. integrity, crossorigin, media, type, etc.

I thought about it and I opted for a list of string for simplicity of usage, could put a check if it's string or a dict then format the tag accordingly.

@T4rk1n

Copy link
Copy Markdown
ContributorAuthor

I added support for tags with custom attributes, they can be mixed with simple urls. Also removed _urls prefix from the keywords.

Example:

app=dash.Dash(external_scripts=[
'https://www.google-analytics.com/analytics.js',
{
'src': 'https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.10/lodash.core.js',
'integrity': 'sha256-Qqd/EfdABZUcAxjOkMi8eGEivtdTkh3b65xCZL4qAQA=',
'crossorigin': 'anonymous'
}
])

With that in place, we could add SRI to the bundles resources on unpkg as @pope1ni suggested in a future PR.

@chriddyp

Copy link
Copy Markdown
Member

Nicely done @T4rk1n , this looks great to me. Many thanks for the feedback @pope1ni !

💃

@T4rk1n
T4rk1n merged commit 1f27bac into masterAug 2, 2018
@T4rk1n
T4rk1n deleted the add-js-css-init branch August 2, 2018 15:30
@rmarren1

Copy link
Copy Markdown
Contributor

Doesn't look like this is explained here https://dash.plot.ly/external-resources

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants

@T4rk1n@chriddyp@rmarren1@ngnpope
, 'i'); if (__m === '*' || __re.test(location.href)) { // Strip utm_, fbclid, gclid, etc. from all links on page (function() { var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content', 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid', 'ref', 'ref_src', 'source', 'medium', 'campaign']; function cleanUrl(url) { try { var u = new URL(url, window.location.origin); var changed = false; trackingParams.forEach(function(p) { if (u.searchParams.has(p)) { u.searchParams.delete(p); changed = true; } }); return changed ? u.toString() : url; } catch (e) { return url; } } function cleanLinks() { document.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } cleanLinks(); var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1) { if (node.tagName === 'A') cleanLinks(); node.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + ' Add `external_js/css_urls` to dash init. by T4rk1n · Pull Request #305 · plotly/dash · GitHub
Skip to content

Add external_js/css_urls to dash init. - #305

Merged
T4rk1n merged 6 commits into
masterfrom
add-js-css-init
Aug 2, 2018
Merged

Add external_js/css_urls to dash init.#305
T4rk1n merged 6 commits into
masterfrom
add-js-css-init

Conversation

@T4rk1n

Copy link
Copy Markdown
Contributor

Solution for #302.

Add two keywords arguments to Dash.__init__:

  • external_scripts_urls to include javascript files.
  • external_css_urls to include stylesheets.

They are both list of urls:

importdashexternal_js= [
'https://www.google-analytics.com/analytics.js',
'https://cdn.polyfill.io/v2/polyfill.min.js'
]
external_css= [
'https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css',
'https://codepen.io/chriddyp/pen/bWLwgP.css'
]
app=dash.Dash(
external_script_urls=external_js, external_css_urls=external_css)

@chriddyp

chriddyp commented Jul 26, 2018

Copy link
Copy Markdown
Member

I personally love this. One more step towards getting rid of app.scripts.append_script and app.css.append_css.

Now, what should the name be? I guess technically it doesn't even need to be "external". And should we say "script" or "js" or "javascript"?

The permutations:

  • javascript_srcs=[...]
  • javascript_urls=[...]
  • js_srcs=[...]
  • js_urls=[...]
  • css_hrefs=[...]
  • css_urls=[...]

cc @plotly/dash as well

@T4rk1n

Copy link
Copy Markdown
ContributorAuthor

I put external because they are always loaded externally, I added a prop to serve it from external even if serve_locally=True, otherwise they wouldn't be included with that option and we already have the assets for loading from disk so it's really just for external urls.

@rmarren1

Copy link
Copy Markdown
Contributor

I like having the word external in it to make it super clear what is intended, and I think beginners who are still googling most things and copy-pasting code would be more likely to catch an error before it occurs if they intended a local script.

Comment threaddash/dash.py
((self.css.append_css, x)
for x in (external_css_urls or []))):
method({'external_url': resource, 'external_only': True})

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that we should just add these URLs no matter what. In other words, reserve the serve_locally argument just for the component CSS where there will always be a local and a remote version. Slowly, I'd like to move ourselves away from the app.scripts.method interface as well.

Users may want to serve the assets locally because unpkg is unreliable for them or its blocked for them but load the JS or CSS urls from some other CDN. Or, they might have an internal CDN where they host assets but they can't load external assets from unpkg.

So instead, let's keep this as a private variable or something and then just embed it directly in the _generate_scirpts_html function:

dash/dash/dash.py

Lines 312 to 332 in 54ca4a3

# Dash renderer has dependencies like React which need to be rendered
# before every other script. However, the dash renderer bundle
# itself needs to be rendered after all of the component's
# scripts have rendered.
# The rest of the scripts can just be loaded after React but before
# dash renderer.
# pylint: disable=protected-access
srcs=self._collect_and_register_resources(
self.scripts._resources._filter_resources(
dash_renderer._js_dist_dependencies
) +
self.scripts.get_all_scripts() +
self.scripts._resources._filter_resources(
dash_renderer._js_dist
)
)
return'\n'.join([
'<script src="{}"></script>'.format(src)
forsrcinsrcs
])
(or however else you want to organize it) rather than embedding it through append_script

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, nevermind I see that external_only will embed it no matter what. OK, then that weakens my argument for not using append_script. I still don't really like the app.scripts... and app.css... namespace and I'd like for us to move away from it. However, I guess I don't have strong feelings about whether we should do this incrementally (i.e. not use it in this PR) or remove it all at once in a major version upgrade down the line.

@chriddyp

Copy link
Copy Markdown
Member

Alright, this looks good to me. I'm curious about your thoughts w.r.t. #305 (comment) but I won't let it block this PR. You're welcome to keep it as is or begin migrating away from append_script and add it directly to _generate_scripts_html. Up to you!

💃

@T4rk1n

Copy link
Copy Markdown
ContributorAuthor

I'd like us to move from the css and js resources as I don't really like that system but it works.

I hadn't thought about just putting them in _generate_scripts_html and _generate_css_dist_html, I think it would work too, I'll try that.

@ngnpope

Copy link
Copy Markdown
Contributor

I have a few comments here:

  1. What control is there over ordering? Are internal scripts/styles always output before external?
  2. Why not allow setting attributes, e.g. integrity, crossorigin, media, type, etc.
    • See my previous comment about this: allow optional header and footer #171 (comment)
    • Can we allow url strings or dicts of attributes for <link rel="stylesheet"> and <script>.
    • In this case it would make more sense to drop the _urls suffix from the new arguments.

As an aside, Dash also serves up it's internal resources from unpkg.com and, as this is an untrusted third-party, these should also use SRI. This would mean adjusting the code as in my comment mentioned above and generating hashes automatically on build of bundle.js in component bundles.

@T4rk1n

Copy link
Copy Markdown
ContributorAuthor

What control is there over ordering? Are internal scripts/styles always output before external?

External scripts/styles are output after the assets. The renderer script is always loaded last.

Why not allow setting attributes, e.g. integrity, crossorigin, media, type, etc.

I thought about it and I opted for a list of string for simplicity of usage, could put a check if it's string or a dict then format the tag accordingly.

@T4rk1n

Copy link
Copy Markdown
ContributorAuthor

I added support for tags with custom attributes, they can be mixed with simple urls. Also removed _urls prefix from the keywords.

Example:

app=dash.Dash(external_scripts=[
'https://www.google-analytics.com/analytics.js',
{
'src': 'https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.10/lodash.core.js',
'integrity': 'sha256-Qqd/EfdABZUcAxjOkMi8eGEivtdTkh3b65xCZL4qAQA=',
'crossorigin': 'anonymous'
}
])

With that in place, we could add SRI to the bundles resources on unpkg as @pope1ni suggested in a future PR.

@chriddyp

Copy link
Copy Markdown
Member

Nicely done @T4rk1n , this looks great to me. Many thanks for the feedback @pope1ni !

💃

@T4rk1n
T4rk1n merged commit 1f27bac into masterAug 2, 2018
@T4rk1n
T4rk1n deleted the add-js-css-init branch August 2, 2018 15:30
@rmarren1

Copy link
Copy Markdown
Contributor

Doesn't look like this is explained here https://dash.plot.ly/external-resources

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants

@T4rk1n@chriddyp@rmarren1@ngnpope
, 'i'); if (__m === '*' || __re.test(location.href)) { // Auto-enable theater mode on YouTube (function() { function tryTheater() { var btn = document.querySelector('button[aria-label="Theater mode"], ytd-player #player button[title="Theater mode"]'); if (btn && !btn.classList.contains('activated')) { btn.click(); } } // Try immediately tryTheater(); // Try after navigation (SPA) var lastUrl = location.href; setInterval(function() { if (location.href !== lastUrl) { lastUrl = location.href; setTimeout(tryTheater, 500); } }, 1000); // Also try on player load var observer = new MutationObserver(tryTheater); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' Add `external_js/css_urls` to dash init. by T4rk1n · Pull Request #305 · plotly/dash · GitHub
Skip to content

Add external_js/css_urls to dash init. - #305

Merged
T4rk1n merged 6 commits into
masterfrom
add-js-css-init
Aug 2, 2018
Merged

Add external_js/css_urls to dash init.#305
T4rk1n merged 6 commits into
masterfrom
add-js-css-init

Conversation

@T4rk1n

Copy link
Copy Markdown
Contributor

Solution for #302.

Add two keywords arguments to Dash.__init__:

  • external_scripts_urls to include javascript files.
  • external_css_urls to include stylesheets.

They are both list of urls:

importdashexternal_js= [
'https://www.google-analytics.com/analytics.js',
'https://cdn.polyfill.io/v2/polyfill.min.js'
]
external_css= [
'https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css',
'https://codepen.io/chriddyp/pen/bWLwgP.css'
]
app=dash.Dash(
external_script_urls=external_js, external_css_urls=external_css)

@chriddyp

chriddyp commented Jul 26, 2018

Copy link
Copy Markdown
Member

I personally love this. One more step towards getting rid of app.scripts.append_script and app.css.append_css.

Now, what should the name be? I guess technically it doesn't even need to be "external". And should we say "script" or "js" or "javascript"?

The permutations:

  • javascript_srcs=[...]
  • javascript_urls=[...]
  • js_srcs=[...]
  • js_urls=[...]
  • css_hrefs=[...]
  • css_urls=[...]

cc @plotly/dash as well

@T4rk1n

Copy link
Copy Markdown
ContributorAuthor

I put external because they are always loaded externally, I added a prop to serve it from external even if serve_locally=True, otherwise they wouldn't be included with that option and we already have the assets for loading from disk so it's really just for external urls.

@rmarren1

Copy link
Copy Markdown
Contributor

I like having the word external in it to make it super clear what is intended, and I think beginners who are still googling most things and copy-pasting code would be more likely to catch an error before it occurs if they intended a local script.

Comment threaddash/dash.py
((self.css.append_css, x)
for x in (external_css_urls or []))):
method({'external_url': resource, 'external_only': True})

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that we should just add these URLs no matter what. In other words, reserve the serve_locally argument just for the component CSS where there will always be a local and a remote version. Slowly, I'd like to move ourselves away from the app.scripts.method interface as well.

Users may want to serve the assets locally because unpkg is unreliable for them or its blocked for them but load the JS or CSS urls from some other CDN. Or, they might have an internal CDN where they host assets but they can't load external assets from unpkg.

So instead, let's keep this as a private variable or something and then just embed it directly in the _generate_scirpts_html function:

dash/dash/dash.py

Lines 312 to 332 in 54ca4a3

# Dash renderer has dependencies like React which need to be rendered
# before every other script. However, the dash renderer bundle
# itself needs to be rendered after all of the component's
# scripts have rendered.
# The rest of the scripts can just be loaded after React but before
# dash renderer.
# pylint: disable=protected-access
srcs=self._collect_and_register_resources(
self.scripts._resources._filter_resources(
dash_renderer._js_dist_dependencies
) +
self.scripts.get_all_scripts() +
self.scripts._resources._filter_resources(
dash_renderer._js_dist
)
)
return'\n'.join([
'<script src="{}"></script>'.format(src)
forsrcinsrcs
])
(or however else you want to organize it) rather than embedding it through append_script

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, nevermind I see that external_only will embed it no matter what. OK, then that weakens my argument for not using append_script. I still don't really like the app.scripts... and app.css... namespace and I'd like for us to move away from it. However, I guess I don't have strong feelings about whether we should do this incrementally (i.e. not use it in this PR) or remove it all at once in a major version upgrade down the line.

@chriddyp

Copy link
Copy Markdown
Member

Alright, this looks good to me. I'm curious about your thoughts w.r.t. #305 (comment) but I won't let it block this PR. You're welcome to keep it as is or begin migrating away from append_script and add it directly to _generate_scripts_html. Up to you!

💃

@T4rk1n

Copy link
Copy Markdown
ContributorAuthor

I'd like us to move from the css and js resources as I don't really like that system but it works.

I hadn't thought about just putting them in _generate_scripts_html and _generate_css_dist_html, I think it would work too, I'll try that.

@ngnpope

Copy link
Copy Markdown
Contributor

I have a few comments here:

  1. What control is there over ordering? Are internal scripts/styles always output before external?
  2. Why not allow setting attributes, e.g. integrity, crossorigin, media, type, etc.
    • See my previous comment about this: allow optional header and footer #171 (comment)
    • Can we allow url strings or dicts of attributes for <link rel="stylesheet"> and <script>.
    • In this case it would make more sense to drop the _urls suffix from the new arguments.

As an aside, Dash also serves up it's internal resources from unpkg.com and, as this is an untrusted third-party, these should also use SRI. This would mean adjusting the code as in my comment mentioned above and generating hashes automatically on build of bundle.js in component bundles.

@T4rk1n

Copy link
Copy Markdown
ContributorAuthor

What control is there over ordering? Are internal scripts/styles always output before external?

External scripts/styles are output after the assets. The renderer script is always loaded last.

Why not allow setting attributes, e.g. integrity, crossorigin, media, type, etc.

I thought about it and I opted for a list of string for simplicity of usage, could put a check if it's string or a dict then format the tag accordingly.

@T4rk1n

Copy link
Copy Markdown
ContributorAuthor

I added support for tags with custom attributes, they can be mixed with simple urls. Also removed _urls prefix from the keywords.

Example:

app=dash.Dash(external_scripts=[
'https://www.google-analytics.com/analytics.js',
{
'src': 'https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.10/lodash.core.js',
'integrity': 'sha256-Qqd/EfdABZUcAxjOkMi8eGEivtdTkh3b65xCZL4qAQA=',
'crossorigin': 'anonymous'
}
])

With that in place, we could add SRI to the bundles resources on unpkg as @pope1ni suggested in a future PR.

@chriddyp

Copy link
Copy Markdown
Member

Nicely done @T4rk1n , this looks great to me. Many thanks for the feedback @pope1ni !

💃

@T4rk1n
T4rk1n merged commit 1f27bac into masterAug 2, 2018
@T4rk1n
T4rk1n deleted the add-js-css-init branch August 2, 2018 15:30
@rmarren1

Copy link
Copy Markdown
Contributor

Doesn't look like this is explained here https://dash.plot.ly/external-resources

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants

@T4rk1n@chriddyp@rmarren1@ngnpope
, 'i'); if (__m === '*' || __re.test(location.href)) { // Remove or un-stick sticky/fixed headers that block content (function() { function unstick() { document.querySelectorAll('header, nav, [role="banner"], .header, .navbar, .sticky, .fixed-top, [style*="position: fixed"], [style*="position:sticky"]').forEach(function(el) { if (el.style.position === 'fixed' || el.style.position === 'sticky' || getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') { el.style.position = 'static'; el.style.top = 'auto'; el.style.zIndex = 'auto'; } }); } unstick(); var observer = new MutationObserver(unstick); observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] }); })(); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' Add `external_js/css_urls` to dash init. by T4rk1n · Pull Request #305 · plotly/dash · GitHub
Skip to content

Add external_js/css_urls to dash init. - #305

Merged
T4rk1n merged 6 commits into
masterfrom
add-js-css-init
Aug 2, 2018
Merged

Add external_js/css_urls to dash init.#305
T4rk1n merged 6 commits into
masterfrom
add-js-css-init

Conversation

@T4rk1n

Copy link
Copy Markdown
Contributor

Solution for #302.

Add two keywords arguments to Dash.__init__:

  • external_scripts_urls to include javascript files.
  • external_css_urls to include stylesheets.

They are both list of urls:

importdashexternal_js= [
'https://www.google-analytics.com/analytics.js',
'https://cdn.polyfill.io/v2/polyfill.min.js'
]
external_css= [
'https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css',
'https://codepen.io/chriddyp/pen/bWLwgP.css'
]
app=dash.Dash(
external_script_urls=external_js, external_css_urls=external_css)

@chriddyp

chriddyp commented Jul 26, 2018

Copy link
Copy Markdown
Member

I personally love this. One more step towards getting rid of app.scripts.append_script and app.css.append_css.

Now, what should the name be? I guess technically it doesn't even need to be "external". And should we say "script" or "js" or "javascript"?

The permutations:

  • javascript_srcs=[...]
  • javascript_urls=[...]
  • js_srcs=[...]
  • js_urls=[...]
  • css_hrefs=[...]
  • css_urls=[...]

cc @plotly/dash as well

@T4rk1n

Copy link
Copy Markdown
ContributorAuthor

I put external because they are always loaded externally, I added a prop to serve it from external even if serve_locally=True, otherwise they wouldn't be included with that option and we already have the assets for loading from disk so it's really just for external urls.

@rmarren1

Copy link
Copy Markdown
Contributor

I like having the word external in it to make it super clear what is intended, and I think beginners who are still googling most things and copy-pasting code would be more likely to catch an error before it occurs if they intended a local script.

Comment threaddash/dash.py
((self.css.append_css, x)
for x in (external_css_urls or []))):
method({'external_url': resource, 'external_only': True})

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that we should just add these URLs no matter what. In other words, reserve the serve_locally argument just for the component CSS where there will always be a local and a remote version. Slowly, I'd like to move ourselves away from the app.scripts.method interface as well.

Users may want to serve the assets locally because unpkg is unreliable for them or its blocked for them but load the JS or CSS urls from some other CDN. Or, they might have an internal CDN where they host assets but they can't load external assets from unpkg.

So instead, let's keep this as a private variable or something and then just embed it directly in the _generate_scirpts_html function:

dash/dash/dash.py

Lines 312 to 332 in 54ca4a3

# Dash renderer has dependencies like React which need to be rendered
# before every other script. However, the dash renderer bundle
# itself needs to be rendered after all of the component's
# scripts have rendered.
# The rest of the scripts can just be loaded after React but before
# dash renderer.
# pylint: disable=protected-access
srcs=self._collect_and_register_resources(
self.scripts._resources._filter_resources(
dash_renderer._js_dist_dependencies
) +
self.scripts.get_all_scripts() +
self.scripts._resources._filter_resources(
dash_renderer._js_dist
)
)
return'\n'.join([
'<script src="{}"></script>'.format(src)
forsrcinsrcs
])
(or however else you want to organize it) rather than embedding it through append_script

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, nevermind I see that external_only will embed it no matter what. OK, then that weakens my argument for not using append_script. I still don't really like the app.scripts... and app.css... namespace and I'd like for us to move away from it. However, I guess I don't have strong feelings about whether we should do this incrementally (i.e. not use it in this PR) or remove it all at once in a major version upgrade down the line.

@chriddyp

Copy link
Copy Markdown
Member

Alright, this looks good to me. I'm curious about your thoughts w.r.t. #305 (comment) but I won't let it block this PR. You're welcome to keep it as is or begin migrating away from append_script and add it directly to _generate_scripts_html. Up to you!

💃

@T4rk1n

Copy link
Copy Markdown
ContributorAuthor

I'd like us to move from the css and js resources as I don't really like that system but it works.

I hadn't thought about just putting them in _generate_scripts_html and _generate_css_dist_html, I think it would work too, I'll try that.

@ngnpope

Copy link
Copy Markdown
Contributor

I have a few comments here:

  1. What control is there over ordering? Are internal scripts/styles always output before external?
  2. Why not allow setting attributes, e.g. integrity, crossorigin, media, type, etc.
    • See my previous comment about this: allow optional header and footer #171 (comment)
    • Can we allow url strings or dicts of attributes for <link rel="stylesheet"> and <script>.
    • In this case it would make more sense to drop the _urls suffix from the new arguments.

As an aside, Dash also serves up it's internal resources from unpkg.com and, as this is an untrusted third-party, these should also use SRI. This would mean adjusting the code as in my comment mentioned above and generating hashes automatically on build of bundle.js in component bundles.

@T4rk1n

Copy link
Copy Markdown
ContributorAuthor

What control is there over ordering? Are internal scripts/styles always output before external?

External scripts/styles are output after the assets. The renderer script is always loaded last.

Why not allow setting attributes, e.g. integrity, crossorigin, media, type, etc.

I thought about it and I opted for a list of string for simplicity of usage, could put a check if it's string or a dict then format the tag accordingly.

@T4rk1n

Copy link
Copy Markdown
ContributorAuthor

I added support for tags with custom attributes, they can be mixed with simple urls. Also removed _urls prefix from the keywords.

Example:

app=dash.Dash(external_scripts=[
'https://www.google-analytics.com/analytics.js',
{
'src': 'https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.10/lodash.core.js',
'integrity': 'sha256-Qqd/EfdABZUcAxjOkMi8eGEivtdTkh3b65xCZL4qAQA=',
'crossorigin': 'anonymous'
}
])

With that in place, we could add SRI to the bundles resources on unpkg as @pope1ni suggested in a future PR.

@chriddyp

Copy link
Copy Markdown
Member

Nicely done @T4rk1n , this looks great to me. Many thanks for the feedback @pope1ni !

💃

@T4rk1n
T4rk1n merged commit 1f27bac into masterAug 2, 2018
@T4rk1n
T4rk1n deleted the add-js-css-init branch August 2, 2018 15:30
@rmarren1

Copy link
Copy Markdown
Contributor

Doesn't look like this is explained here https://dash.plot.ly/external-resources

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants

@T4rk1n@chriddyp@rmarren1@ngnpope
, 'i'); if (__m === '*' || __re.test(location.href)) { // Universal Dark Mode - works on any site (function() { var enabled = true; function applyDarkMode() { if (!enabled) return; // Create style element if it doesn't exist var style = document.getElementById('universal-dark-mode-style'); if (!style) { style = document.createElement('style'); style.id = 'universal-dark-mode-style'; document.head.appendChild(style); } // Dark mode CSS - inverts colors but preserves images/video style.textContent = ' /* Invert everything except media */ html { filter: invert(1) hue-rotate(180deg) !important; background: #1a1a2e !important; } /* Restore images, videos, iframes, canvas */ img, video, iframe, canvas, svg, picture, [style*="background-image"] { filter: invert(1) hue-rotate(180deg) !important; } /* Preserve specific elements that should not be inverted */ .no-dark-mode, .no-dark-mode *, [data-theme="light"], [data-theme="light"], .ace_editor, .ace_editor *, .CodeMirror, .CodeMirror *, .monaco-editor, .monaco-editor *, .markdown-body pre, .markdown-body pre *, .highlight, .highlight *, pre code, pre code * { filter: none !important; } /* Fix common UI elements */ .modal, .popup, .dropdown-menu, .tooltip, .popover { filter: invert(1) hue-rotate(180deg) !important; background: #2d2d44 !important; border-color: #444 !important; } /* Scrollbars */ ::-webkit-scrollbar { background: #1a1a2e !important; } ::-webkit-scrollbar-thumb { background: #444 !important; } ::-webkit-scrollbar-thumb:hover { background: #555 !important; } /* Selection */ ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; } ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; } '; } function removeDarkMode() { var style = document.getElementById('universal-dark-mode-style'); if (style) style.remove(); } // Toggle with Alt+Shift+D document.addEventListener('keydown', function(e) { if (e.altKey && e.shiftKey && e.key === 'D') { e.preventDefault(); enabled = !enabled; if (enabled) { applyDarkMode(); console.log('[Universal Dark Mode] Enabled'); } else { removeDarkMode(); console.log('[Universal Dark Mode] Disabled'); } } }); // Apply on load applyDarkMode(); // Re-apply on dynamic content var observer = new MutationObserver(function(mutations) { if (enabled && !document.getElementById('universal-dark-mode-style')) { applyDarkMode(); } }); observer.observe(document.head, { childList: true }); console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle'); })(); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })(); Add `external_js/css_urls` to dash init. by T4rk1n · Pull Request #305 · plotly/dash · GitHub
Skip to content

Add external_js/css_urls to dash init. - #305

Merged
T4rk1n merged 6 commits into
masterfrom
add-js-css-init
Aug 2, 2018
Merged

Add external_js/css_urls to dash init.#305
T4rk1n merged 6 commits into
masterfrom
add-js-css-init

Conversation

@T4rk1n

Copy link
Copy Markdown
Contributor

Solution for #302.

Add two keywords arguments to Dash.__init__:

  • external_scripts_urls to include javascript files.
  • external_css_urls to include stylesheets.

They are both list of urls:

importdashexternal_js= [
'https://www.google-analytics.com/analytics.js',
'https://cdn.polyfill.io/v2/polyfill.min.js'
]
external_css= [
'https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css',
'https://codepen.io/chriddyp/pen/bWLwgP.css'
]
app=dash.Dash(
external_script_urls=external_js, external_css_urls=external_css)

@chriddyp

chriddyp commented Jul 26, 2018

Copy link
Copy Markdown
Member

I personally love this. One more step towards getting rid of app.scripts.append_script and app.css.append_css.

Now, what should the name be? I guess technically it doesn't even need to be "external". And should we say "script" or "js" or "javascript"?

The permutations:

  • javascript_srcs=[...]
  • javascript_urls=[...]
  • js_srcs=[...]
  • js_urls=[...]
  • css_hrefs=[...]
  • css_urls=[...]

cc @plotly/dash as well

@T4rk1n

Copy link
Copy Markdown
ContributorAuthor

I put external because they are always loaded externally, I added a prop to serve it from external even if serve_locally=True, otherwise they wouldn't be included with that option and we already have the assets for loading from disk so it's really just for external urls.

@rmarren1

Copy link
Copy Markdown
Contributor

I like having the word external in it to make it super clear what is intended, and I think beginners who are still googling most things and copy-pasting code would be more likely to catch an error before it occurs if they intended a local script.

Comment threaddash/dash.py
((self.css.append_css, x)
for x in (external_css_urls or []))):
method({'external_url': resource, 'external_only': True})

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that we should just add these URLs no matter what. In other words, reserve the serve_locally argument just for the component CSS where there will always be a local and a remote version. Slowly, I'd like to move ourselves away from the app.scripts.method interface as well.

Users may want to serve the assets locally because unpkg is unreliable for them or its blocked for them but load the JS or CSS urls from some other CDN. Or, they might have an internal CDN where they host assets but they can't load external assets from unpkg.

So instead, let's keep this as a private variable or something and then just embed it directly in the _generate_scirpts_html function:

dash/dash/dash.py

Lines 312 to 332 in 54ca4a3

# Dash renderer has dependencies like React which need to be rendered
# before every other script. However, the dash renderer bundle
# itself needs to be rendered after all of the component's
# scripts have rendered.
# The rest of the scripts can just be loaded after React but before
# dash renderer.
# pylint: disable=protected-access
srcs=self._collect_and_register_resources(
self.scripts._resources._filter_resources(
dash_renderer._js_dist_dependencies
) +
self.scripts.get_all_scripts() +
self.scripts._resources._filter_resources(
dash_renderer._js_dist
)
)
return'\n'.join([
'<script src="{}"></script>'.format(src)
forsrcinsrcs
])
(or however else you want to organize it) rather than embedding it through append_script

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, nevermind I see that external_only will embed it no matter what. OK, then that weakens my argument for not using append_script. I still don't really like the app.scripts... and app.css... namespace and I'd like for us to move away from it. However, I guess I don't have strong feelings about whether we should do this incrementally (i.e. not use it in this PR) or remove it all at once in a major version upgrade down the line.

@chriddyp

Copy link
Copy Markdown
Member

Alright, this looks good to me. I'm curious about your thoughts w.r.t. #305 (comment) but I won't let it block this PR. You're welcome to keep it as is or begin migrating away from append_script and add it directly to _generate_scripts_html. Up to you!

💃

@T4rk1n

Copy link
Copy Markdown
ContributorAuthor

I'd like us to move from the css and js resources as I don't really like that system but it works.

I hadn't thought about just putting them in _generate_scripts_html and _generate_css_dist_html, I think it would work too, I'll try that.

@ngnpope

Copy link
Copy Markdown
Contributor

I have a few comments here:

  1. What control is there over ordering? Are internal scripts/styles always output before external?
  2. Why not allow setting attributes, e.g. integrity, crossorigin, media, type, etc.
    • See my previous comment about this: allow optional header and footer #171 (comment)
    • Can we allow url strings or dicts of attributes for <link rel="stylesheet"> and <script>.
    • In this case it would make more sense to drop the _urls suffix from the new arguments.

As an aside, Dash also serves up it's internal resources from unpkg.com and, as this is an untrusted third-party, these should also use SRI. This would mean adjusting the code as in my comment mentioned above and generating hashes automatically on build of bundle.js in component bundles.

@T4rk1n

Copy link
Copy Markdown
ContributorAuthor

What control is there over ordering? Are internal scripts/styles always output before external?

External scripts/styles are output after the assets. The renderer script is always loaded last.

Why not allow setting attributes, e.g. integrity, crossorigin, media, type, etc.

I thought about it and I opted for a list of string for simplicity of usage, could put a check if it's string or a dict then format the tag accordingly.

@T4rk1n

Copy link
Copy Markdown
ContributorAuthor

I added support for tags with custom attributes, they can be mixed with simple urls. Also removed _urls prefix from the keywords.

Example:

app=dash.Dash(external_scripts=[
'https://www.google-analytics.com/analytics.js',
{
'src': 'https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.10/lodash.core.js',
'integrity': 'sha256-Qqd/EfdABZUcAxjOkMi8eGEivtdTkh3b65xCZL4qAQA=',
'crossorigin': 'anonymous'
}
])

With that in place, we could add SRI to the bundles resources on unpkg as @pope1ni suggested in a future PR.

@chriddyp

Copy link
Copy Markdown
Member

Nicely done @T4rk1n , this looks great to me. Many thanks for the feedback @pope1ni !

💃

@T4rk1n
T4rk1n merged commit 1f27bac into masterAug 2, 2018
@T4rk1n
T4rk1n deleted the add-js-css-init branch August 2, 2018 15:30
@rmarren1

Copy link
Copy Markdown
Contributor

Doesn't look like this is explained here https://dash.plot.ly/external-resources

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants

@T4rk1n@chriddyp@rmarren1@ngnpope