Skip to content

Commit 57e0b26

Browse files
committed
#293: Add options to include full plotly js or use require to load plotly in generated html docs
1 parent 7bf912e commit 57e0b26

File tree

12 files changed

+257
-81
lines changed

12 files changed

+257
-81
lines changed

src/Plotly.NET/ChartAPI/Chart.fs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3304,21 +3304,21 @@ type Chart =
33043304
/// </summary>
33053305
/// <param name="AdditionalHeadTags">Additional tags that will be included in the document's head </param>
33063306
/// <param name="Description">HTML tags that appear below the chart in HTML docs</param>
3307-
/// <param name="PlotlyCDN">The PlotlyCDN address used in html docs</param>
3307+
/// <param name="PlotlyJSReference">Sets how plotly is referenced in the head of html docs. When CDN, a script tag that references the plotly.js CDN is included in the output. When Full, a script tag containing the plotly.js source code (~3MB) is included in the output. HTML files generated with this option are fully self-contained and can be used offline</param>
33083308
[<CompiledName("WithDisplayOptionsStyle")>]
33093309
static member withDisplayOptionsStyle
33103310
(
33113311
[<Optional; DefaultParameterValue(null)>] ?AdditionalHeadTags: XmlNode list,
33123312
[<Optional; DefaultParameterValue(null)>] ?Description: XmlNode list,
3313-
[<Optional; DefaultParameterValue(null)>] ?PlotlyCDN: string
3313+
[<Optional; DefaultParameterValue(null)>] ?PlotlyJSReference: PlotlyJSReference
33143314
) =
33153315
(fun (ch: GenericChart) ->
33163316

33173317
let displayOpts' =
33183318
DisplayOptions.init (
33193319
?AdditionalHeadTags = AdditionalHeadTags,
33203320
?Description = Description,
3321-
?PlotlyCDN = PlotlyCDN
3321+
?PlotlyJSReference = PlotlyJSReference
33223322
)
33233323

33243324
GenericChart.addDisplayOptions displayOpts' ch)

src/Plotly.NET/ChartAPI/GenericChart.fs

Lines changed: 65 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -8,21 +8,55 @@ open Giraffe.ViewEngine
88

99
type HTML() =
1010

11+
static member CreateChartScript(
12+
data: string,
13+
layout: string,
14+
config: string,
15+
plotlyReference: PlotlyJSReference,
16+
guid: string
17+
) =
18+
match plotlyReference with
19+
| Require r ->
20+
script [_type "text/javascript"] [
21+
rawText (
22+
Globals.REQUIREJS_SCRIPT_TEMPLATE
23+
.Replace("[REQUIRE_SRC]",r)
24+
.Replace("[SCRIPTID]",guid.Replace("-",""))
25+
.Replace("[ID]",guid)
26+
.Replace("[DATA]",data)
27+
.Replace("[LAYOUT]",layout)
28+
.Replace("[CONFIG]",config)
29+
)]
30+
| _ ->
31+
script [_type "text/javascript"] [
32+
rawText (
33+
Globals.SCRIPT_TEMPLATE
34+
.Replace("[SCRIPTID]",guid.Replace("-",""))
35+
.Replace("[ID]",guid)
36+
.Replace("[DATA]",data)
37+
.Replace("[LAYOUT]",layout)
38+
.Replace("[CONFIG]",config)
39+
)]
40+
41+
1142
static member Doc(
1243
chart,
13-
plotlyCDN,
44+
plotlyReference: PlotlyJSReference,
1445
?AdditionalHeadTags,
1546
?Description
1647
) =
1748
let additionalHeadTags = defaultArg AdditionalHeadTags []
1849
let description = defaultArg Description []
1950

20-
let plotlyScript =
21-
script [_src plotlyCDN] []
51+
let plotlyScriptRef =
52+
match plotlyReference with
53+
| CDN cdn -> script [_src cdn] []
54+
| Full -> script [_type "text/javascript"] [rawText (InternalUtils.getFullPlotlyJS())]
55+
| NoReference | Require _ -> rawText ""
2256

2357
html [] [
2458
head [] [
25-
plotlyScript
59+
plotlyScriptRef
2660
yield! additionalHeadTags
2761
]
2862
body [] [
@@ -34,45 +68,23 @@ type HTML() =
3468
static member CreateChartHTML(
3569
data: string,
3670
layout: string,
37-
config: string
71+
config: string,
72+
plotlyReference: PlotlyJSReference
3873
) =
39-
40-
let scriptContent = """
41-
var renderPlotly_[SCRIPTID] = function() {
42-
var fsharpPlotlyRequire = requirejs.config({context:'fsharp-plotly',paths:{plotly:'https://cdn.plot.ly/plotly-[PLOTLYJS_VERSION].min'}}) || require;
43-
fsharpPlotlyRequire(['plotly'], function(Plotly) {
44-
var data = [DATA];
45-
var layout = [LAYOUT];
46-
var config = [CONFIG];
47-
Plotly.newPlot('[ID]', data, layout, config);
48-
});
49-
};
50-
if ((typeof(requirejs) !== typeof(Function)) || (typeof(requirejs.config) !== typeof(Function))) {
51-
var script = document.createElement("script");
52-
script.setAttribute("src", "https://cdnjs.cloudflare.com/ajax/libs/require.js/2.3.6/require.min.js");
53-
script.onload = function(){
54-
renderPlotly_[SCRIPTID]();
55-
};
56-
document.getElementsByTagName("head")[0].appendChild(script);
57-
}
58-
else {
59-
renderPlotly_[SCRIPTID]();
60-
}
61-
"""
6274
let guid = Guid.NewGuid().ToString()
6375

76+
let chartScript =
77+
HTML.CreateChartScript(
78+
data = data,
79+
layout = layout,
80+
config = config,
81+
plotlyReference = plotlyReference,
82+
guid = guid
83+
)
84+
6485
[
6586
div [_id guid] [comment "Plotly chart will be drawn inside this DIV"]
66-
script [_type "text/javascript"] [
67-
rawText (
68-
scriptContent
69-
.Replace("[PLOTLYJS_VERSION]",Globals.PLOTLYJS_VERSION)
70-
.Replace("[SCRIPTID]",guid.Replace("-",""))
71-
.Replace("[ID]",guid)
72-
.Replace("[DATA]",data)
73-
.Replace("[LAYOUT]",layout)
74-
.Replace("[CONFIG]",config)
75-
)]
87+
chartScript
7688
]
7789

7890
/// Module to represent a GenericChart
@@ -228,13 +240,18 @@ module GenericChart =
228240

229241
let displayOpts = getDisplayOptions gChart
230242

243+
let description = displayOpts |> DisplayOptions.getDescription
244+
245+
let plotlyReference = displayOpts |> DisplayOptions.getPlotlyPlotlyReference
246+
231247
div [] [
232248
yield! HTML.CreateChartHTML(
233-
tracesJson,
234-
layoutJson,
235-
configJson
249+
data = tracesJson,
250+
layout = layoutJson,
251+
config = configJson,
252+
plotlyReference = plotlyReference
236253
)
237-
yield! displayOpts |> DisplayOptions.getDescription
254+
yield! description
238255
]
239256

240257
let toChartHTML gChart =
@@ -263,15 +280,16 @@ module GenericChart =
263280

264281
let description = (displayOpts |> DisplayOptions.getDescription)
265282

266-
let plotlyCDN = (displayOpts |> DisplayOptions.getPlotlyCDN)
283+
let plotlyReference = displayOpts |> DisplayOptions.getPlotlyPlotlyReference
267284

268285
HTML.Doc(
269286
chart = HTML.CreateChartHTML(
270-
tracesJson,
271-
layoutJson,
272-
configJson
287+
data = tracesJson,
288+
layout = layoutJson,
289+
config = configJson,
290+
plotlyReference = plotlyReference
273291
),
274-
plotlyCDN = plotlyCDN,
292+
plotlyReference = plotlyReference,
275293
AdditionalHeadTags = additionalHeadTags,
276294
Description = description
277295
)

src/Plotly.NET/Defaults.fs

Lines changed: 2 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -23,12 +23,7 @@ module Defaults =
2323
let mutable DefaultConfig =
2424
Config.init (Responsive = true)
2525

26-
let mutable DefaultDisplayOptions =
27-
DisplayOptions.init(
28-
AdditionalHeadTags = [],
29-
Description = [],
30-
PlotlyCDN = $"https://cdn.plot.ly/plotly-{Globals.PLOTLYJS_VERSION}.min.js"
31-
)
26+
let mutable DefaultDisplayOptions = DisplayOptions.initCDNOnly()
3227

3328
/// The default chart template. Default: ChartTemplates.plotly
3429
let mutable DefaultTemplate =
@@ -39,10 +34,5 @@ module Defaults =
3934
DefaultWidth <- 600
4035
DefaultHeight <- 600
4136
DefaultConfig <- Config.init (Responsive = true)
42-
DefaultDisplayOptions <-
43-
DisplayOptions.init(
44-
AdditionalHeadTags = [],
45-
Description = [],
46-
PlotlyCDN = $"https://cdn.plot.ly/plotly-{Globals.PLOTLYJS_VERSION}.min.js"
47-
)
37+
DefaultDisplayOptions <- DisplayOptions.initCDNOnly()
4838
DefaultTemplate <- ChartTemplates.plotly

src/Plotly.NET/DisplayOptions/DisplayOptions.fs

Lines changed: 23 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,18 @@ open DynamicObj
44
open System.Runtime.InteropServices
55
open Giraffe.ViewEngine
66

7+
8+
///Sets how plotly is referenced in the head of html docs.
9+
type PlotlyJSReference =
10+
/// The url for a script tag that references the plotly.js CDN When
11+
| CDN of string
12+
/// Full plotly.js source code (~3MB) is included in the output. HTML files generated with this option are fully self-contained and can be used offline
13+
| Full
14+
/// Use requirejs to reference plotlyjs from a url
15+
| Require of string
16+
//include no plotlyjs script at all. This can be helpfull when embedding the output into a document that already references plotly.
17+
| NoReference
18+
719
type DisplayOptions() =
820
inherit DynamicObj()
921

@@ -12,35 +24,35 @@ type DisplayOptions() =
1224
/// </summary>
1325
/// <param name="AdditionalHeadTags">Additional tags that will be included in the document's head </param>
1426
/// <param name="Description">HTML tags that appear below the chart in HTML docs</param>
15-
/// <param name="PlotlyCDN">The PlotlyCDN address used in html docs</param>
27+
/// <param name="PlotlyJSReference">Sets how plotly is referenced in the head of html docs. When CDN, a script tag that references the plotly.js CDN is included in the output. When Full, a script tag containing the plotly.js source code (~3MB) is included in the output. HTML files generated with this option are fully self-contained and can be used offline</param>
1628
static member init (
1729
[<Optional; DefaultParameterValue(null)>] ?AdditionalHeadTags: XmlNode list,
1830
[<Optional; DefaultParameterValue(null)>] ?Description: XmlNode list,
19-
[<Optional; DefaultParameterValue(null)>] ?PlotlyCDN: string
31+
[<Optional; DefaultParameterValue(null)>] ?PlotlyJSReference: PlotlyJSReference
2032
) =
2133
DisplayOptions()
2234
|> DisplayOptions.style(
2335
?AdditionalHeadTags = AdditionalHeadTags,
2436
?Description = Description,
25-
?PlotlyCDN = PlotlyCDN
37+
?PlotlyJSReference = PlotlyJSReference
2638
)
2739

2840
/// <summary>
2941
/// Returns a function sthat applies the given styles to a DisplayOptions object
3042
/// </summary>
3143
/// <param name="AdditionalHeadTags">Additional tags that will be included in the document's head </param>
3244
/// <param name="Description">HTML tags that appear below the chart in HTML docs</param>
33-
/// <param name="PlotlyCDN">The PlotlyCDN address used in html docs</param>
45+
/// <param name="PlotlyJSReference">Sets how plotly is referenced in the head of html docs. When CDN, a script tag that references the plotly.js CDN is included in the output. When Full, a script tag containing the plotly.js source code (~3MB) is included in the output. HTML files generated with this option are fully self-contained and can be used offline</param>
3446
static member style (
3547
[<Optional; DefaultParameterValue(null)>] ?AdditionalHeadTags: XmlNode list,
3648
[<Optional; DefaultParameterValue(null)>] ?Description: XmlNode list,
37-
[<Optional; DefaultParameterValue(null)>] ?PlotlyCDN: string
49+
[<Optional; DefaultParameterValue(null)>] ?PlotlyJSReference: PlotlyJSReference
3850
) =
3951
(fun (displayOpts: DisplayOptions) ->
4052

4153
AdditionalHeadTags |> DynObj.setValueOpt displayOpts "AdditionalHeadTags"
4254
Description |> DynObj.setValueOpt displayOpts "Description"
43-
PlotlyCDN |> DynObj.setValueOpt displayOpts "PlotlyCDN"
55+
PlotlyJSReference|> DynObj.setValueOpt displayOpts "PlotlyJSReference"
4456

4557
displayOpts)
4658

@@ -50,7 +62,7 @@ type DisplayOptions() =
5062
static member initCDNOnly() =
5163
DisplayOptions()
5264
|> DisplayOptions.style(
53-
PlotlyCDN = Globals.PLOTLYJS_VERSION
65+
PlotlyJSReference = CDN $"https://cdn.plot.ly/plotly-{Globals.PLOTLYJS_VERSION}.min.js"
5466
)
5567

5668
/// <summary>
@@ -126,12 +138,12 @@ type DisplayOptions() =
126138
)
127139
)
128140

129-
static member setPlotlyCDN(plotlyCDN: string) =
141+
static member setPlotlyReference(plotlyReference: PlotlyJSReference) =
130142
(fun (displayOpts: DisplayOptions) ->
131-
plotlyCDN |> DynObj.setValue displayOpts "PlotlyCDN"
143+
plotlyReference |> DynObj.setValue displayOpts "PlotlyJSReference"
132144
displayOpts
133145
)
134146

135-
static member tryGetPlotlyCDN (displayOpts: DisplayOptions) = displayOpts.TryGetTypedValue<string>("PlotlyCDN")
147+
static member tryGetPlotlyReference (displayOpts: DisplayOptions) = displayOpts.TryGetTypedValue<PlotlyJSReference>("PlotlyJSReference")
136148

137-
static member getPlotlyCDN (displayOpts: DisplayOptions) = displayOpts |> DisplayOptions.tryGetPlotlyCDN |> Option.defaultValue ""
149+
static member getPlotlyPlotlyReference (displayOpts: DisplayOptions) = displayOpts |> DisplayOptions.tryGetPlotlyReference |> Option.defaultValue (PlotlyJSReference.NoReference)

src/Plotly.NET/Globals.fs

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,38 @@ open Giraffe.ViewEngine
88
/// The plotly js version loaded from cdn in rendered html docs
99
let [<Literal>] PLOTLYJS_VERSION = "2.18.1"
1010

11+
let [<Literal>] SCRIPT_TEMPLATE ="""var renderPlotly_[SCRIPTID] = function() {
12+
var data = [DATA];
13+
var layout = [LAYOUT];
14+
var config = [CONFIG];
15+
Plotly.newPlot('[ID]', data, layout, config);
16+
};
17+
renderPlotly_[SCRIPTID]();
18+
"""
19+
20+
let [<Literal>] REQUIREJS_SCRIPT_TEMPLATE ="""
21+
var renderPlotly_[SCRIPTID] = function() {
22+
var fsharpPlotlyRequire = requirejs.config({context:'fsharp-plotly',paths:{plotly:'[REQUIRE_SRC]'}}) || require;
23+
fsharpPlotlyRequire(['plotly'], function(Plotly) {
24+
var data = [DATA];
25+
var layout = [LAYOUT];
26+
var config = [CONFIG];
27+
Plotly.newPlot('[ID]', data, layout, config);
28+
});
29+
};
30+
if ((typeof(requirejs) !== typeof(Function)) || (typeof(requirejs.config) !== typeof(Function))) {
31+
var script = document.createElement("script");
32+
script.setAttribute("src", "https://cdnjs.cloudflare.com/ajax/libs/require.js/2.3.6/require.min.js");
33+
script.onload = function(){
34+
renderPlotly_[SCRIPTID]();
35+
};
36+
document.getElementsByTagName("head")[0].appendChild(script);
37+
}
38+
else {
39+
renderPlotly_[SCRIPTID]();
40+
}
41+
"""
42+
1143
///
1244
let internal JSON_CONFIG =
1345
JsonSerializerSettings(

src/Plotly.NET/InternalUtils.fs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ module internal InternalUtils
33

44
open DynamicObj
55
open System.Runtime.InteropServices
6+
open System.IO
7+
open System.Reflection
68

79
let combineOptSeqs (first: seq<'A> option) (second: seq<'A> option) =
810
match first, second with
@@ -18,6 +20,12 @@ let combineOptLists (first: List<'A> option) (second: List<'A> option) =
1820
| None, Some s -> Some s
1921
| _ -> None
2022

23+
let getFullPlotlyJS() =
24+
let assembly = Assembly.GetExecutingAssembly()
25+
use str = assembly.GetManifestResourceStream("Plotly.NET.plotly-2.18.1.min.js")
26+
use r = new StreamReader(str)
27+
r.ReadToEnd()
28+
2129
[<AutoOpen>]
2230
module DynObj =
2331

src/Plotly.NET/Plotly.NET.fsproj

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,8 @@
3636
<ItemGroup>
3737
<None Include="RELEASE_NOTES.md" />
3838
<None Include="..\..\docs\img\logo.png" Pack="true" PackagePath="\" />
39+
<EmbeddedResource Include="plotly-2.18.1.min.js" />
40+
<EmbeddedResource Include="plotly-2.18.1.min.js.LICENSE.txt" />
3941
<Compile Include="Globals.fs" />
4042
<Compile Include="InternalUtils.fs" />
4143
<Compile Include="CommonAbstractions\ColorKeyword.fs" />

src/Plotly.NET/plotly-2.18.1.min.js

Lines changed: 8 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)